Bash의 변수에서 한 줄씩 읽는 방법은 무엇입니까?

Bash의 변수에서 한 줄씩 읽는 방법은 무엇입니까?

명령의 여러 줄 출력을 포함하는 변수가 있습니다. 변수에서 출력을 한 줄씩 읽는 가장 효율적인 방법은 무엇입니까?

예를 들어:

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보다 더 나은 명령을 생각해 낼 수 있습니다.

관련 정보