IFS = 읽기 대신 IFS = 읽기가 자주 사용되는 이유는 무엇입니까? 읽는 동안 ..`?


81

각 반복마다 설정을 반복하지 않기 위해 일반적인 관행은 IFS 설정을 while 루프 외부에 두는 것 같습니다 ... 이것은이 원숭이까지 그랬던 것처럼 습관적인 "원숭이 참조, 원숭이 수행"스타일입니까? 나는 man read를 읽 거나 여기에 미묘한 (또는 명백히 명백한) 함정을 놓치고 있습니까?

답변:


82

함정은

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'..


이 질문의 두 번째 부분이 여기에 병합되었으므로 여기 에서 관련 답변을 제거했습니다.


자, 잠재적 인 '트랩'은 외부 IFS를 재설정하는 것을 무시 하는 것 같습니다 ... 그러나 다른 무언가도 있는지 궁금합니다 ... 여기에서 아주 열렬하게 테스트하고 있습니다. while의 명령 목록에서 IFS를 설정하면 콜론 뒤에 오는지 여부에 따라 qute가 다르게 작동합니다. 나는이 행동 (아직)을 이해하지 못하며, 이제이 단계에서 특별히 고려해야 할 사항이 있는지 궁금합니다. while IFS=X read에 분할하지 않습니다 X,하지만 while IFS=X; read하지 ...
Peter.O

(당신은 의미 준결승 두 번째는? 바로, 대장)을 while훨씬 이해가되지 않습니다 -에 대한 조건 while 이 세미콜론에서를, 그래서 거기에 실제 루프는 ...없는 read하나의 요소 루프 내부의 바로 첫 번째 명령이됩니다 ... 또는하지 ? [정보] 무엇 do다음 ..?
rozcietrzewiacz

1
아니오, 잠깐만 요-당신 말이 맞습니다 while(조건 이전에 do).
rozcietrzewiacz

아 .. 분명히, 당신이 ... 알다시피 ...하지만 세미콜론을 좋아하지 않는 것 같습니다 ... -zero exit code) ... 트랩이 완전히 다른 섹터에 있는지 궁금합니다 . while 명령 목록의 작동 방식 이해 왜 IFS=작동하지만 IFS=X작동하지 않습니다 ... (또는 어쩌면 내가 이것에 대해 OD를했습니다 .. 커피 브레이크가 필요했습니다 :)
Peter.O

1
내가 (이전 코멘트에서 언급 한 바와 같이) .. 그것은 재미 보인다, 내 업데이 트를 움직일 때 $ rozcietrzewiacz은 .. 죄송합니다 ... 나는 당신의 업데이 트를 발견하지 않았 부터 이해하는 ...하지만 심지어 밤 -에 대한 나와 같은 새, 그것은 매우 늦었습니다 ... (방금 아침 새를 들었습니다 :) ... 그 말은, 나는 조금 들었고 당신의 예를 읽었습니다. 당신이 그것을 확실히 가지고 있지만, 나는 자야합니다 :) ... 이것은 거의 유레카입니다! 순간 ... 감사
Peter.O

45

신중하게 제작 된 입력 텍스트가 포함 된 예를 살펴 보겠습니다.

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와 사이에 이중 공간 worldline있습니다. 한편, 초기 공간은 먹었다. 때문이다 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에서 지정하지 않으며 실제로 쉘에 따라 다릅니다.


4
감사합니다 Gilles .. 아주 좋은 가이드 투어 .. ( 'set -f'를 의미 했습니까?) ...... 이제 독자들에게 이미 말한 내용을 다시 언급하기 위해, 나는 있었던 문제를 강조하고 싶습니다 나는 그것을 잘못된 길로보고있다. 가장 먼저 구조물 있다는 사실이다 while IFS= read(이후에 세미콜론이없는이 =)입니다 하지 의 특별한 형태 while또는 IFS또는 read예 : 구조체는 일반적입니다 ..이. anyvar=anyvalue anycommand. ;after 설정 이 없으면 localanyvar 의 범위가로 설정 됩니다 . while-do / done 루프는 의 로컬 범위 100 % 관련이 없습니다 . anyvar anycommandany_var
Peter.O

3

, 및 관용구 (명령에 따라 대 스크립트 / 쉘 전체 변수 범위 지정) IFS사이 의 (이미 명확하게) 범위 지정 차이점 외에도 테이크 홈 레슨은 IFS 변수 인 경우 입력 행 의 선행 후행 공백 을 잃는다는 것입니다 공백으로 설정되어 있습니다.while IFS='' readIFS=''; while readwhile IFS=''; readIFS

파일 경로가 처리되는 경우 이는 심각한 결과를 초래할 수 있습니다.

따라서 IFS 변수를 빈 문자열로 설정하는 것은 행의 선행 및 후행 공백이 제거되지 않기 때문에 나쁜 생각입니다.

참조 : Bash, 파일에서 한 줄씩 읽기, 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"
)

'rm * file * with * spaces *'를 사용한 후 탁월한 시연, 정리 +1
amdn

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.