텍스트 파일(또는 파이프로 연결된 출력, 여기서는 중요하지 않음)이 있습니다.
memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
memcached.cmd_set 17641364
memcached.cmd_flush 0
이 명령을 사용하면 cat test.txt | while read i; do echo $i; done
상당히 예상되는 출력이 생성됩니다.
memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
etc
하지만 반복하면 for i in $(cat test.txt); do echo $i; done
다른 내용이 표시됩니다.
memcached.uptime
1061374
memcached.curr_connections
480
memcached.cmd_get
478962548
etc
문제는: 왜? ? ?
답변1
존재하다:
cat test.txt | while read i; do echo $i; done
당신은 꽤 많은 쉘 스크립팅 나쁜 습관을 벼락치기로 만들었습니다.
먼저 언급해야 하지만쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?.
예를 들어 다음을 입력해 보세요.
-n
/*/*/*/../../../*/*/*
foo\
bar
쉘 루프를 꼭 사용해야 한다면 아마도 다음과 같아야 할 것입니다:
{
while IFS= read <&3 -r i; do
printf '%s\n' "$i" || exit
done
[ -z "$i" ] || printf %s "$i" || exit
} 3< test.txt
존재하다
for i in $(cat test.txt); do echo $i; done
이는 또 다른 나쁜 습관으로 대체될 수 있습니다. 여기에 인용하지 않은 타당한 이유가 있습니다 $(cat test.txt)
. 분할+글로브 연산자의 분할 부분을 원하지만 분할할 항목을 지정하고 글로브 부분을 비활성화하는 것을 잊어버렸습니다.
IFS='
' # split on newline only. The default value of $IFS
# contains space, tab and newline which explains why you see
# one word per line
set -o noglob # disable glob
for i in $(cat test.txt); do
printf '%s\n' "$i" || exit
done
루프를 시작하기 전에 빈 줄은 여전히 건너뛰고 파일 내용을 읽고 메모리에 저장합니다(여러 번).
답변2
대답은 $(command)
명령의 원시 출력으로 확장하는 것입니다. 그러면 쉘이 이에 대해 일반적인 단어 분할을 수행합니다. 분리에는 다음이 포함됩니다.어느공백은 단어 구분 기호로 처리됩니다.
또한 텍스트를 구문 분석하는 중 하나에서 두 가지 다른 작업을 수행하고 있습니다 read
.철사없이 입력단어$(cat)
입력이고 다른 하나는 루프에서 출력을 반복하는 것입니다 for
. 비슷한 결과를 얻을 수도 있습니다 IFS='\n' for i in $(cat test.txt); do echo "$i"; done
.