it-swarm-ko.tech

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

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

예를 들면 다음과 같습니다.

jobs="$(jobs)"
if [ "$jobs" ]; then
    # read lines from $jobs
fi
53
Eugene Yarmash

프로세스 대체와 함께 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")

참고 : shellcheck sc2031 에 따라 서브 쉘을 [미묘하게] 생성하지 않도록 파이프보다 프로세스 서브 스트 레이션을 사용하는 것이 좋습니다.

또한 변수 jobs의 이름을 지정하면 일반적인 Shell 명령의 이름이기 때문에 혼동 될 수 있습니다.

70
dogbane

한 줄씩 명령 출력을 처리하려면 ( 설명 ) :

jobs |
while IFS= read -r line; do
  process "$line"
done

변수에 이미 데이터가있는 경우 :

printf %s "$foo" | …

printf %s "$foo"echo "$foo"와 거의 동일하지만 문자 그대로 $foo를 인쇄하지만 echo "$foo"$foo를 echo 명령의 옵션으로 해석 할 수 있습니다. -로 시작하고 일부 쉘에서 $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
}

비어 있지 않은 행에 대한 작업이 충분하고 입력이 크지 않은 경우 Word 분할을 사용할 수 있습니다.

IFS='
'
set -f
for line in $(jobs); do
  # process line
done
set +f
unset IFS

설명 : IFS을 하나의 줄 바꿈으로 설정하면 기본 설정의 공백 문자와 달리 줄 바꿈에서만 Word 분할이 발생합니다. set -f는 글 로빙 (예 : 와일드 카드 확장)을 해제합니다. 그렇지 않으면 명령 대체 $(jobs) 또는 변수 대체 $foo의 결과로 발생합니다. for 루프는 명령 출력에서 ​​비어 있지 않은 모든 라인 인 $(jobs)의 모든 부분에 작용합니다. 마지막으로 globbing 및 IFS 설정을 복원하십시오.

문제점 : while 루프를 사용하면 서브 쉘에서 실행되고 모든 변수가 손실됩니다. 솔루션 : for 루프 사용

# change delimiter (IFS) to new line.
IFS_BAK=$IFS
IFS=$'\n'

for line in $variableWithSeveralLines; do
 echo "$line"

 # return IFS back if you need to split new line by spaces:
 IFS=$IFS_BAK
 IFS_BAK=
 lineConvertedToArraySplittedBySpaces=( $line )
 echo "{lineConvertedToArraySplittedBySpaces[0]}"
 # return IFS back to newline for "for" loop
 IFS_BAK=$IFS
 IFS=$'\n'

done 

# return delimiter to previous value
IFS=$IFS_BAK
IFS_BAK=
16
Dima
jobs="$(jobs)"
while IFS= read
do
    echo $REPLY
done <<< "$jobs"

참고 문헌 :

11
l0b0

최근 bash 버전에서 mapfile 또는 readarray을 사용하여 명령 출력을 배열로 효율적으로 읽습니다.

$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305

면책 조항 : 끔찍한 예이지만 ls보다 더 나은 명령을 사용할 수 있습니다.

10
sehe

<<<를 사용하면 개행으로 구분 된 데이터가 포함 된 변수에서 간단히 읽을 수 있습니다.

while read -r line
do 
  echo "A line of input: $line"
done <<<"$lines"
0
Matthew Herrmann