[arrays] Bash의 배열로 명령 출력 읽기
스크립트의 명령 출력을 배열로 읽어야합니다. 예를 들면 다음과 같습니다.
ps aux | grep | grep | x
다음과 같이 한 줄씩 출력을 제공합니다.
10
20
30
명령 출력의 값을 배열로 읽어야하며 배열의 크기가 3보다 작 으면 몇 가지 작업을 수행합니다.
답변
다른 답변이 명령의 출력에 공백이 포함 된 경우 (오히려 자주 인) 중단 또는 같은 문자 glob에한다 *
, ?
, [...]
.
요소 당 한 줄씩 배열에서 명령의 출력을 얻으려면 기본적으로 세 가지 방법이 있습니다.
-
Bash≥4를 사용
mapfile
하면 가장 효율적입니다.mapfile -t my_array < <( my_command )
-
그렇지 않으면 출력을 읽는 루프 (느리지 만 안전함) :
my_array=() while IFS= read -r line; do my_array+=( "$line" ) done < <( my_command )
-
댓글에서 Charles Duffy가 제안한대로 (감사합니다!), 다음은 2 번의 루프 방법보다 더 잘 수행 될 수 있습니다.
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )
이 양식을 정확히 사용했는지 확인하십시오. 즉, 다음이 있는지 확인하십시오.
IFS=$'\n'
read
명령문 과 같은 줄에 : 명령문에 대해서만 환경 변수IFS
를read
설정합니다 . 따라서 나머지 스크립트에는 전혀 영향을주지 않습니다. 이 변수의 목적은read
EOL 문자에서 스트림을 중단하도록 지시하는 것\n
입니다.-r
: 이건 중요하다. 백 슬래시를 이스케이프 시퀀스로 해석하지 않도록 지시read
합니다.-d ''
:-d
옵션과 인수 사이의 공백에 유의하십시오''
. 여기에 공백을 두지 않으면 Bash가 문을 구문 분석 할 때 따옴표 제거 단계''
에서 사라지기 때문에이 표시되지 않습니다 . 이것은 nil 바이트에서 읽기를 중지하도록 지시 합니다. 어떤 사람들은 그것을라고 쓰지만 실제로는 필요하지 않습니다. 더 나은.read
-d $'\0'
-d ''
-a my_array
스트림을 읽는 동안read
배열을 채우도록 지시my_array
합니다.- 다음 을 반환 하도록 다음
printf '\0'
문 을 사용해야 합니다 . 그렇지 않은 경우 실제로 큰 문제는 아니지만 ( 사용하지 않으면 괜찮은 반환 코드를 받게됩니다. 어차피 그렇게해서는 안됩니다), 그 점을 명심하십시오. 더 깨끗하고 의미 상 정확합니다. 이것은 아무것도 출력하지 않는 와는 다릅니다 . 여기서 읽기를 중지하는 데 필요한 null 바이트를 인쇄합니다 ( 옵션을 기억 하십니까?).my_command
read
0
1
set -e
printf ''
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 -f
globbing을 비활성화 하는 데 사용하는 것이 좋습니다 .하지만 살펴보면 다음 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
이것이 핵심 개념이며 입력은 이전에 삭제 된 것으로 간주됩니다. 즉, 추가 문자 제거, 빈 문자열 처리 등 (이 스레드의 주제에서 벗어남).