[arrays] Bash의 배열로 명령 출력 읽기

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

ps aux | grep | grep | x 

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

10
20
30

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



답변

다른 답변이 명령의 출력에 공백이 포함 된 경우 (오히려 자주 인) 중단 또는 같은 문자 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]")'

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


답변

당신이 사용할 수있는

my_array=( $(<command>) )

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

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

my_array_length=${#my_array[@]}

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


답변

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

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

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

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

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


답변