IFS = 읽기 대신 IFS = 읽기가 자주 사용되는 이유는 무엇입니까? 읽는 동안 ..`? 각 반복마다 설정을 반복하지 않기 위해

각 반복마다 설정을 반복하지 않기 위해 일반적인 관행은 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와 사이에 이중 공간 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에서 지정하지 않으며 실제로 쉘에 따라 다릅니다.


답변

, 및 관용구 (명령에 따라 대 스크립트 / 쉘 전체 변수 범위 지정) 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"
)


답변

Yuzem의 답변에서 영감을 받음

IFS실제 캐릭터 로 설정하고 싶다면 이것이 효과가 있습니다.

iconv -f cp1252 zapni.tv.php | while IFS='#' read -d'#' line
do
  echo "$line"
done


답변