"IFS=;" 대신 "IFS= 읽기"가 자주 사용되는 이유는 무엇입니까?

"IFS=;" 대신 "IFS= 읽기"가 자주 사용되는 이유는 무엇입니까?

일반적인 관행은 각 반복마다 반복적으로 설정하지 않도록 IFS 설정을 while 루프 외부에 두는 것 같습니다... 이것은 단지 관례적인 "원숭이가 보고, 원숭이가 하는" 스타일인가요? 읽다남자들은 읽다, 아니면 여기서 미묘한(또는 노골적으로 명백한) 문제를 놓치고 있는 걸까요?

답변1

함정은

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


이 질문의 두 번째 부분여기에 병합됨, 그래서 여기에서 관련 답변을 삭제했습니다.

답변2

세심하게 제작된 입력 텍스트의 예를 살펴보겠습니다.

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은 개행을 무시하고, backslash-anything은 첫 번째 백슬래시를 무시합니다. 백슬래시가 특별하게 처리되는 것을 방지하기 위해 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바꾸다null 값입니다.

$ 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(null 값으로) IFSread(null 값으로) 설정합니다. 일반적인 경우의 예입니다간단한 명령구문: 변수 할당의 순서(비어 있을 수 있음), 그 뒤에 명령 이름과 해당 인수가 옵니다(또한 언제든지 리디렉션을 도입할 수 있습니다). 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$PATHIFS=; while read …IFS:

IFS물론, 실행 후 그 값을 복원할 수 있습니다 read. 그러나 이를 위해서는 이전 값을 알아야 하며 이는 추가 노력이 필요합니다. IFS= read가장 간단한 방법입니다(그리고 편리하게도 가장 짧은 방법입니다).

1그리고 트랩 신호에 의해 중단 되면 read트랩이 실행되는 동안일 수 있습니다. 이는 POSIX에서 지정하지 않고 실제로 쉘에 따라 다릅니다.

답변3

관용구와 관용구 사이의 (이미 명확한) 범위 지정 차이(명령별 대 스크립트/쉘 범위 변수 범위 지정)를 제외하고 가장 IFS중요한 교훈은 선행 항목을 잃어버린다는 것입니다.while IFS='' readIFS=''; while readwhile IFS=''; readIFS그리고IFS 변수가 공백 포함(포함)으로 설정된 경우 입력 줄의 후행 공백이 입력됩니다.

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

따라서 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"
)

답변4

에서 영감을 받다Yuzem의 답변

실제 캐릭터로 설정하고 싶다면 IFS이것이 나에게 효과적이었습니다.

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

관련 정보