명령의 여러 줄 출력을 포함하는 변수가 있습니다. 변수에서 출력을 한 줄씩 읽는 가장 효율적인 방법은 무엇입니까?
예를 들어:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
답변1
프로세스 대체를 위해 while 루프를 사용할 수 있습니다.
while read -r line
do
echo "$line"
done < <(jobs)
여러 줄 변수를 읽는 가장 좋은 방법은 빈 IFS
변수를 설정하고 printf
변수에 후행 개행 문자를 추가하는 것입니다.
# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
echo "$line"
done < <(printf '%s\n' "$var")
참고: 에 따르면쉘체크 sc2031, 파이프 대신 프로세스 대체를 사용하여 서브쉘 생성을 [미묘하게] 방지합니다.
또한 변수 이름을 지정하면 jobs
일반적인 쉘 명령의 이름이기도 하므로 혼동을 일으킬 수 있습니다.
답변2
명령줄의 출력을 한 줄씩 처리합니다(설명하다):
jobs |
while IFS= read -r line; do
process "$line"
done
변수에 이미 데이터가 있는 경우:
printf %s "$foo" | …
printf %s "$foo"
와 거의 동일 echo "$foo"
하지만 $foo
문자 그대로 인쇄되는 반면, 로 시작하면 echo "$foo"
echo 명령에 대한 옵션으로 해석될 수 있으며 일부 쉘에서는 백슬래시 시퀀스가 확장될 수 있습니다.$foo
-
$foo
일부 셸(ash, bash, pdksh, ksh 또는 zsh는 제외)에서는 파이프의 오른쪽이 별도의 프로세스에서 실행되므로 루프에 설정한 모든 변수가 손실됩니다. 예를 들어, 다음 줄 계산 스크립트는 이러한 셸에서 0을 인쇄합니다.
n=0
printf %s "$foo" |
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
$n
해결책은 스크립트의 나머지 부분(또는 적어도 루프에 값이 필요한 부분)을 명령 목록에 넣는 것입니다.
n=0
printf %s "$foo" | {
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
}
비어 있지 않은 줄에 대한 작업이 충분하고 입력이 그리 크지 않은 경우 단어 분할을 사용할 수 있습니다.
IFS='
'
set -f
for line in $(jobs); do
# process line
done
set +f
unset IFS
설명: IFS
단일 줄 바꿈으로 설정하면 줄 바꿈에서만 단어 분리가 발생합니다(기본 설정의 공백 문자 대신). set -f
명령 대체 또는 변수 대체 $(jobs)
의 결과로 발생하는 와일드카드(즉, 와일드카드 확장)를 끄십시오 $foo
. 루프는 명령 출력 for
의 모든 부분 $(jobs)
, 즉 명령 출력의 공백이 아닌 모든 행에서 작동합니다. 마지막으로 와일드카드 및 IFS
설정을 기본값으로 복원합니다.
답변3
jobs="$(jobs)"
while IFS= read -r line
do
echo "$line"
done <<< "$jobs"
인용하다:
답변4
최신 bash 버전에서는 mapfile
또는를 사용하여 readarray
명령 출력을 배열로 효과적으로 읽습니다 .
$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305
면책 조항: 끔찍한 예이지만 아마도 ls보다 더 나은 명령을 생각해 낼 수 있습니다.