각 반복마다 설정을 반복하지 않기 위해 일반적인 관행은 IFS 설정을 while 루프 외부에 두는 것 같습니다 … 이것은이 원숭이까지 그랬던 것처럼 습관적인 “원숭이 참조, 원숭이 수행”스타일입니까? 나는 man read를 읽 거나 여기에 미묘한 (또는 명백히 명백한) 함정을 놓치고 있습니까?
답변
함정은
IFS=; while read..
IFS
루프 외부에서 전체 쉘 환경을 설정합니다.
while IFS= read
read
호출에 대해서만 재정의합니다 (Bourne 쉘 제외). 루프를 수행하는 것을 확인할 수 있습니다
while IFS= read xxx; ... done
그런 루프 후에 echo "blabalbla $IFS ooooooo"
인쇄
blabalbla
ooooooo
반면에
IFS=; read xxx; ... done
IFS
숙박 재정의 : 지금 echo "blabalbla $IFS ooooooo"
인쇄
blabalbla ooooooo
따라서 두 번째 양식을 사용하는 경우 다음을 재설정해야합니다 IFS=$' \t\n'
..
이 질문의 두 번째 부분이 여기에 병합되었으므로 여기 에서 관련 답변을 제거했습니다.
답변
신중하게 제작 된 입력 텍스트가 포함 된 예를 살펴 보겠습니다.
text=' hello world\
foo\bar'
그것은 공백으로 시작하고 백 슬래시로 끝나는 두 줄입니다. 먼저 주변의주의 사항없이 발생하는 상황을 살펴 보자 read
( 확장 위험없이 printf '%s\n' "$text"
신중하게 인쇄 하는 데 사용 $text
). (아래 $
는 쉘 프롬프트입니다.)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
read
백 슬래시를 먹었습니다. backslash-newline은 개행을 무시하고 백 슬래시는 무엇이든 첫 번째 백 슬래시를 무시합니다. 백 슬래시가 특수하게 처리되지 않도록하기 위해을 사용 read -r
합니다.
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
더 좋습니다. 예상대로 두 줄이 있습니다. 두 줄에는 원하는 내용이 거의 포함되어 있습니다 . 변수 사이 에 있기 때문에 사이 hello
와 사이에 이중 공간 world
이 line
있습니다. 한편, 초기 공간은 먹었다. 때문이다 read
당신이 그것을 변수를 전달로 마지막 변수는 라인의 나머지 부분을 포함하는 것을 제외하고, 많은 단어를 읽고 -하지만 초기 공간이 삭제됩니다 즉, 여전히, 첫 번째 단어로 시작합니다.
따라서 문자 그대로 각 줄을 읽으려면 단어 분리 가 일어나지 않도록해야합니다 . IFS
변수 를 빈 값 으로 설정하면 됩니다.
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
내장 IFS
기간 동안 구체적으로read
설정 한 방법에 주목하십시오 . 의 실행을 위해 특별히 IFS= read -r line
환경 변수 IFS
를 빈 값으로 설정합니다 read
. 다음은 일반적인 간단한 명령 구문 의 예입니다. 변수 할당의 순서는 비어 있고 순서는 명령 이름과 해당 인수입니다 (또한 언제든 리디렉션을 던질 수 있음). read
내장 이기 때문에 변수는 실제로 외부 프로세스 환경에서 끝나지 않습니다. 그럼에도 불구하고의 가치 는 실행 $IFS
하는 한 우리가 할당하는 것 read
입니다 ¹. 즉주의 read
가 아닌 특별한 내장 , 할당이 기간 동안 만 지속 않도록.
따라서 이에 IFS
의존 할 수있는 다른 지침 에 대한 가치를 변경하지 않도록주의 하고 있습니다. 이 코드는 주변 코드의 IFS
초기 설정 에 관계없이 작동 하며 루프 내부의 코드가에 의존하더라도 문제를 일으키지 않습니다 IFS
.
콜론으로 구분 된 경로에서 파일을 찾는이 코드 스 니펫과 대조하십시오. 파일 이름 목록은 파일에서 한 줄에 하나씩 파일 이름을 읽습니다.
IFS=":"; set -f
while IFS= read -r name; do
for dir in $PATH; do
## At this point, "$IFS" is still ":"
if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
done
done <filenames.txt
루프가 while IFS=; read -r name; do …
인 경우 콜론으로 구분 된 구성 요소로 for dir in $PATH
분할되지 않습니다 $PATH
. 코드가 IFS=; while read …
인 경우 루프 본문에 IFS
설정되지 않은 것이 더 분명합니다 :
.
물론, IFS
실행 후의 값을 복원 할 수 있습니다 read
. 그러나이를 위해서는 이전의 가치를 알아야합니다. 이는 추가적인 노력입니다. IFS= read
간단한 방법입니다 (그리고 편리하게도 가장 짧은 방법입니다).
¹ 그리고 read
트랩이 실행되는 동안 트랩 된 신호에 의해 중단 된 경우 POSIX에서 지정하지 않으며 실제로 쉘에 따라 다릅니다.
답변
, 및 관용구 (명령에 따라 대 스크립트 / 쉘 전체 변수 범위 지정) IFS
사이 의 (이미 명확하게) 범위 지정 차이점 외에도 테이크 홈 레슨은 IFS 변수 인 경우 입력 행 의 선행 및 후행 공백 을 잃는다는 것입니다 공백으로 설정되어 있습니다.while IFS='' read
IFS=''; while read
while IFS=''; read
IFS
파일 경로가 처리되는 경우 이는 심각한 결과를 초래할 수 있습니다.
따라서 IFS 변수를 빈 문자열로 설정하는 것은 행의 선행 및 후행 공백이 제거되지 않기 때문에 나쁜 생각입니다.
(
shopt -s nullglob
touch ' file with spaces '
IFS=$' \t\n' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
IFS='' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
)
답변
Yuzem의 답변에서 영감을 받음
IFS
실제 캐릭터 로 설정하고 싶다면 이것이 효과가 있습니다.
iconv -f cp1252 zapni.tv.php | while IFS='#' read -d'#' line
do
echo "$line"
done