Bash의 배열로 명령 출력 읽기


111

스크립트의 명령 출력을 배열로 읽어야합니다. 예를 들면 다음과 같습니다.

ps aux | grep | grep | x 

다음과 같이 한 줄씩 출력을 제공합니다.

10
20
30

명령 출력의 값을 배열로 읽어야하며 배열의 크기가 3보다 작 으면 몇 가지 작업을 수행합니다.


5
안녕하세요 @barp, 귀하의 질문에 답하십시오. 귀하의 유형이 전체 커뮤니티를 낭비하지 않도록하십시오.
James

9
@James는 질문에 대답하지 않는다는 사실이 문제가 아닙니다. 이것은 Q / A 사이트입니다. 그는 대답으로 표시 하지 않았습니다 . 그는 그들을 표시해야합니다. 힌트. @ barp
DDPWNAGE

4
@barp, 질문을 답변으로 표시하십시오.
smonff

관련 : 프로세스 대체를 통해 명령의 출력을 읽는 것은 파일에서 읽는 것과 유사하므로 Bash에서 파일 내용을 반복 합니다.
codeforester

답변:


161

다른 답변이 명령의 출력에 공백이 포함 된 경우 (오히려 자주 인) 중단 또는 같은 문자 glob에한다 *, ?, [...].

요소 당 한 줄씩 배열에서 명령의 출력을 얻으려면 기본적으로 세 가지 방법이 있습니다.

  1. Bash≥4를 사용 mapfile하면 가장 효율적입니다.

    mapfile -t my_array < <( my_command )
  2. 그렇지 않으면 출력을 읽는 루프 (느리지 만 안전함) :

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
  3. 댓글에서 Charles Duffy가 제안한대로 (감사합니다!), 다음은 2 번의 루프 방법보다 더 잘 수행 될 수 있습니다.

    IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )

    이 양식을 정확히 사용했는지 확인하십시오. 즉, 다음이 있는지 확인하십시오.

    • IFS=$'\n' read명령문 과 같은 줄에 : 명령문에 대해서만 환경 변수 IFS read 설정합니다 . 따라서 나머지 스크립트에는 전혀 영향을주지 않습니다. 이 변수의 목적은 readEOL 문자에서 스트림을 중단하도록 지시하는 것 \n입니다.
    • -r: 이건 중요하다. 백 슬래시를 이스케이프 시퀀스로 해석하지 않도록 지시 read 합니다.
    • -d '': -d옵션과 인수 사이의 공백에 유의하십시오 ''. 여기에 공백을 두지 않으면 Bash가 문을 구문 분석 할 때 따옴표 제거 단계 ''에서 사라지기 때문에이 표시되지 않습니다 . 이것은 nil 바이트에서 읽기를 중지하도록 지시 합니다. 어떤 사람들은 그것을라고 쓰지만 실제로는 필요하지 않습니다. 더 나은.read-d $'\0'-d ''
    • -a my_array스트림을 읽는 동안 read배열을 채우도록 지시 my_array합니다.
    • 다음 을 반환 하도록 다음 printf '\0' 사용해야 합니다 . 그렇지 않은 경우 실제로 큰 문제는 아니지만 ( 사용하지 않으면 괜찮은 반환 코드를 받게됩니다. 어차피 그렇게해서는 안됩니다), 그 점을 명심하십시오. 더 깨끗하고 의미 상 정확합니다. 이것은 아무것도 출력하지 않는 와는 다릅니다 . 여기서 읽기를 중지하는 데 필요한 null 바이트를 인쇄합니다 ( 옵션을 기억 하십니까?).my_commandread01set -eprintf ''printf '\0'read-d ''

가능하다면, 즉 코드가 Bash≥4에서 실행될 것이라고 확신한다면 첫 번째 방법을 사용하십시오. 그리고 더 짧다는 것을 알 수 있습니다.

를 사용 read하려면 루프 (방법 2)가 행을 읽을 때 일부 처리를 수행하려는 경우 방법 3보다 유리할 수 있습니다. 직접 액세스 할 수 있습니다 ( $line제가 준 예제 의 변수를 통해 ). 또한 이미 읽은 행에 액세스 할 수 있습니다 ( ${my_array[@]}제가 제공 한 예제 의 배열 을 통해 ).

참고 mapfile각 라인에 eval'd 콜백을 할 수있는 방법을 제공합니다 읽기, 사실 당신도 매이 콜백 전화를 말할 수있는 N의 라인 읽기를; 한 번 봐 가지고 help mapfile하고 옵션 -C-c거기에 있습니다. (이것에 대한 제 의견은 약간 투박하지만, 할 일이 간단한 경우에 가끔 사용할 수 있다는 것입니다. 왜 이것이 처음부터 구현되었는지도 모르겠습니다!).


이제 다음과 같은 방법을 사용하는 이유를 알려 드리겠습니다.

my_array=( $( my_command) )

공백이 있으면 깨집니다.

$ # I'm using this command to test:
$ echo "one two"; echo "three four"
one two
three four
$ # Now I'm going to use the broken method:
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # As you can see, the fields are not the lines
$
$ # Now look at the correct method:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!

그런 다음 일부 사람들은 IFS=$'\n'문제를 해결하는 데 사용 하는 것이 좋습니다 .

$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!

하지만 이제 glob 과 함께 다른 명령을 사용하겠습니다 .

$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?

그 이유 t는 현재 디렉토리에 라는 파일이 있고 ...이 파일 이름이 glob 과 일치하기 때문입니다 [three four].이 시점에서 일부 사람들은 set -fglobbing을 비활성화 하는 데 사용하는 것이 좋습니다 .하지만 살펴보면 다음 IFS과 같이 변경 하고 사용해야 set -f합니다. 깨진 기술 (그리고 당신은 그것을 실제로 고치지도 않습니다)! 할 때 우리가 정말하고 있다는 싸우는 하지, 쉘 쉘 작업 .

$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'

여기서 우리는 셸을 사용하고 있습니다!


4
이것은 대단합니다. mapfile전에 들어 본 적이 없었습니다. 정확히 몇 년 동안 놓쳤던 것입니다. 의 최신 버전 bash에는 멋진 새 기능이 너무 많아서 며칠 동안 문서를 읽고 멋진 치트 시트를 작성해야합니다.
Gene Pavlovsky

6
Btw, < <(command)쉘 스크립트 에서이 구문을 사용 하려면 shebang 행은 #!/bin/bash다음과 같아야합니다.로 실행 #!/bin/sh하면 bash가 구문 오류와 함께 종료됩니다.
Gene Pavlovsky

1
@GenePavlovsky의 유용한 메모를 확장하면 스크립트는 bash my_script.shsh 명령이 아닌 bash 명령으로도 실행되어야합니다sh my_script.sh
Vito

2
@Vito : 사실,이 답변은 배쉬입니다 만, 엄격하게 호환 POSIX 쉘도 배열을 구현하지 않는 (으로 이것이 문제가되지 않습니다 shdash에 대한 모든에서 배열에 대해 알고하지 않습니다, 물론 제외 위치 매개 변수 $@배열).
gniourf_gniourf

3
bash 4.0이 필요하지 않은 또 다른 대안으로 고려 IFS=$'\n' read -r -d '' -a my_array < <(my_command && printf '\0')하십시오. 둘 다 bash 3.x에서 올바르게 작동하며 실패한 종료 상태 my_command를 통해 read.
Charles Duffy

86

당신이 사용할 수있는

my_array=( $(<command>) )

명령의 출력을 <command>배열 에 저장합니다 my_array.

다음을 사용하여 해당 배열의 길이에 액세스 할 수 있습니다.

my_array_length=${#my_array[@]}

이제 길이가에 저장됩니다 my_array_length.


19
$ (command)의 출력에 공백이 있고 공백이있는 여러 줄이 있으면 어떻게됩니까? "$ (command)"를 추가하고 모든 행의 모든 ​​출력을 배열의 첫 번째 [0] 요소에 배치합니다.
ikwyl6

3
@ ikwyl6 해결 방법은 명령 출력을 변수에 할당 한 다음이를 사용하여 배열을 만들거나 배열에 추가하는 것입니다. VAR="$(<command>)"다음 my_array=("$VAR")또는my_array+=("$VAR")
비토

10

파일과 디렉토리 이름 (현재 폴더 아래)을 배열에 넣고 항목 수를 계산한다고 가정 해보십시오. 스크립트는 다음과 같습니다.

my_array=( `ls` )
my_array_length=${#my_array[@]}
echo $my_array_length

또는 다음 스크립트를 추가하여이 배열을 반복 할 수 있습니다.

for element in "${my_array[@]}"
do
   echo "${element}"
done

이것이 핵심 개념이며 입력은 이전에 삭제 된 것으로 간주됩니다. 즉, 추가 문자 제거, 빈 문자열 처리 등 (이 스레드의 주제에서 벗어남).


3
위의 답변에서 언급 한 이유로 끔찍한 생각
휴 버트 Grzeskowiak
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.