이 사이트의 다음 스레드와 StackOverflow는 IFS
작동 방식 을 이해하는 데 도움이되었습니다 .
그러나 여전히 몇 가지 간단한 질문이 있습니다. 나는 미래의 독자들에게 도움이 될 것이라고 생각하기 때문에 같은 게시물에 질문하기로 결정했습니다.
Q1. IFS
일반적으로 “필드 분할”의 맥락에서 논의됩니다. 가 필드 분할은 같은 단어 분할 ?
IFS 값이 널이면 필드 분할이 수행되지 않습니다.
null IFS=
로 설정 한 것과 동일하게 설정 IFS
되어 있습니까? 이것이 empty string
너무 설정되어 있다는 의미 입니까?
Q3 : POSIX 사양 에서 다음을 읽었 습니다.
IFS가 설정되어 있지 않으면 쉘은 IFS 값이 다음과 같이 작동합니다.
<space>, <tab> and <newline>
기본값을 복원하고 싶다고 가정 해보십시오 IFS
. 어떻게합니까? (보다 구체적으로 어떻게 참조 할 <tab>
및 <newline>
?)
Q4 : 마지막으로이 코드는 어떻게 됩니까?
while IFS= read -r line
do
echo $line
done < /path_to_text_file
첫 줄을 다음과 같이 바꾸면
while read -r line # Use the default IFS value
또는
while IFS=' ' read -r line
답변
- 예, 동일합니다.
- 예.
- bash 및 유사한 쉘에서 다음과 같은 작업을 수행 할 수
IFS=$' \t\n'
있습니다. 그렇지 않으면을 사용하여 리터럴 제어 코드를 삽입 할 수 있습니다[space] CTRL+V [tab] CTRL+V [enter]
. 그러나이 작업을 수행하려는 경우 다른 변수를 사용하여 이전IFS
값 을 임시로 저장 한 다음 나중에 복원하거나var=foo command
구문 을 사용하여 한 명령에 대해 임시로 재정의하는 것이 좋습니다. -
- 첫 번째 코드 스 니펫은
$line
단어 분리를 수행 할 필드 구분 기호가 없으므로 전체 행을 그대로 읽습니다 . 그러나 많은 쉘이 문자열을 저장하기 위해 cstring을 사용하기 때문에, NUL의 첫 번째 인스턴스는 여전히 조기 종료되는 것처럼 보일 수 있습니다. - 두 번째 코드 스 니펫은 입력의 정확한 사본을에 넣지 않을 수 있습니다
$line
. 예를 들어 연속 된 필드 구분자가 여러 개인 경우 첫 번째 요소의 단일 인스턴스로 만들어집니다. 이것은 종종 주변 공백의 손실로 인식됩니다. - 세 번째 코드 스 니펫은 두 번째 코드와 동일하게 수행됩니다 (단, 일반 스페이스, 탭 또는 개행 문자는 제외).
- 첫 번째 코드 스 니펫은
답변
Q1 : 그렇습니다. “필드 분할”과 “단어 분할”은 동일한 개념에 대한 두 가지 용어입니다.
Q2 : 그렇습니다. IFS
설정되지 않은 경우 (예 🙂 , 공백, 탭 및 줄 바꿈 으로 설정 한 unset IFS
것과 같습니다 . 빈 값으로 설정된 경우 (즉, 여기서 “널 (null)”이 의미하는 경우 (예 : 또는 또는 )) 필드 분할이 전혀 수행되지 않습니다 (및 의 첫 번째 문자를 일반적으로 사용하는 공백 문자를 사용함).IFS
$' \t\n'
IFS
IFS=
IFS=''
IFS=""
$*
$IFS
Q3 : 기본 IFS
동작 을 원하면을 사용할 수 있습니다 unset IFS
. IFS
이 기본값으로 명시 적 으로 설정 하려면 리터럴 문자 공백, 탭, 줄 바꿈을 작은 따옴표로 묶을 수 있습니다. ksh93, bash 또는 zsh에서는을 사용할 수 있습니다 IFS=$' \t\n'
. 소스 파일에 리터럴 탭 문자를 사용하지 않으려면 다음을 사용할 수 있습니다.
IFS=" $(echo t | tr t \\t)
"
Q4 : IFS
빈 값으로 read -r line
설정 line
하면 종료 개행을 제외한 전체 행으로 설정 됩니다. 을 사용 IFS=" "
하면 줄의 시작과 끝에 공백이 잘립니다. 기본값 인 IFS
으로 탭과 공백이 잘립니다.
답변
Q1. 필드 분할.
필드 분할은 단어 분할과 동일합니까?
예, 둘 다 같은 생각을 가리 킵니다.
Q2 : IFS 는 언제 null 입니까?
IFS=''
빈 문자열과 동일한 null로 설정 합니까?
예, 세 가지 모두 동일합니다. 필드 / 단어 분할을 수행하지 않아야합니다. 또한 이것은 (와 마찬가지로 echo "$*"
) 인쇄 필드에 영향을 미치며 모든 필드는 공간없이 함께 연결됩니다.
Q3 : (파트 a) Unset IFS.
POSIX 사양 에서 다음을 읽었습니다 .
IFS가 설정되어 있지 않으면 쉘은 IFS 값이 <space> <tab> <newline> 인 것처럼 동작합니다 .
다음과 정확히 동일합니다.
를 사용하면
unset IFS
쉘은 IFS가 기본값 인 것처럼 동작합니다.
이는 ‘필드 분할’이 기본 IFS 값과 정확히 동일하거나 설정되지 않음을 의미합니다.
그렇다고 IFS가 모든 조건에서 동일한 방식으로 작동한다는 의미는 아닙니다. 보다 구체적으로 실행 OldIFS=$IFS
하면 var OldIFS
가 기본값이 아닌 null로 설정됩니다. 이와 같이 IFS를 다시 설정하려고하면 IFS=OldIFS
IFS가 null로 설정되어 이전과 같이 설정되지 않은 상태로 유지됩니다. 조심해 !!.
Q3 : (파트 b) IFS 복원.
IFS 값을 기본값으로 복원하는 방법 IFS의 기본값을 복원하고 싶다고 가정 해보십시오. 어떻게합니까? (더 구체적으로, <tab> 및 <newline>을 어떻게 참조합니까?)
zsh, ksh 및 bash (AFAIK)의 경우 IFS를 다음과 같이 기본값으로 설정할 수 있습니다.
IFS=$' \t\n' # works with zsh, ksh, bash.
완료, 당신은 다른 것을 읽을 필요가 없습니다.
그러나 sh에 대해 IFS를 다시 설정해야하는 경우 복잡해질 수 있습니다.
단점을 제외하고 가장 쉬운 방법부터 완료 해 봅시다 (복잡성 제외).
IFS를 설정 해제합니다.
우리는 단지 unset IFS
(위의 Q3 부분 a를 읽으십시오).
스왑 문자.
이 문제를 해결하려면 tab과 newline의 값을 바꾸면 IFS의 값을 더 간단하게 설정 한 다음 동일한 방식으로 작동합니다.
IFS를 <space> <newline> <tab>으로 설정하십시오 .
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3. 간단한? 해결책:
IFS를 올바르게 설정해야하는 하위 스크립트가있는 경우 언제든지 수동으로 작성할 수 있습니다.
IFS = ' '
어디 수동으로 입력 순서가 있었다 : IFS=
'spacetabnewline'실제로 제대로 (당신이 확인해야하는 경우,이 대답을 편집) 위의 입력 된 순서. 그러나 브라우저가 공백을 쥐어 짜거나 숨기기 때문에 브라우저에서 복사 / 붙여 넣기가 중단됩니다. 위에서 설명한대로 코드를 공유하기가 어렵습니다.
완벽한 솔루션.
안전하게 복사 할 수있는 코드를 작성하려면 일반적으로 명확한 인쇄 가능한 이스케이프가 필요합니다.
예상 값을 “생성하는”코드가 필요합니다. 그러나 개념적으로 정확 하더라도이 코드는 후행을 설정하지 않습니다 \n
.
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
대부분의 셸 에서 확장 후 모든 줄 바꿈 $(...)
또는 `...`
명령 대체가 제거 되기 때문에 발생합니다 .
우리는 sh에 대한 트릭 을 사용해야합니다 .
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
다른 방법은 bash에서 환경 값으로 IFS를 설정 한 다음 (예 : 환경을 통해 IFS가 설정되도록 허용하는 버전) sh를 호출하는 것입니다.
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
간단히 말해 sh는 IFS를 기본값으로 재설정하는 것이 매우 이상한 모험입니다.
Q4 : 실제 코드에서 :
마지막으로,이 코드는 어떻게 :
while IFS= read -r line do echo $line done < /path_to_text_file
첫 줄을 다음과 같이 바꾸면
while read -r line # Use the default IFS value
또는
while IFS=' ' read -r line
첫째 : echo $line
(var NOT 인용 된)이 Porpouse에 있는지 여부는 알 수 없습니다. 읽기에는없는 두 번째 레벨의 ‘필드 분할’을 소개합니다. 둘 다 대답하겠습니다. 🙂
이 코드를 사용하면 확인할 수 있습니다. 유용한 xxd 가 필요합니다 :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
나는 얻다:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
첫 번째 값은 올바른 값입니다 IFS=
'spacetabnewline'
다음 줄은 var $a
가 가진 모든 16 진 값이며, 각 읽기 명령에 주어질 때 줄 바꿈 ‘0a’입니다.
IFS가 null 인 다음 행은 ‘필드 분할’을 수행하지 않지만 줄 바꿈이 제거됩니다 (예상대로).
다음 세 줄은 IFS에 공백이 포함되어 있으므로 초기 공백을 제거하고 var 행을 나머지 잔액으로 설정하십시오.
마지막 네 줄은 따옴표없는 변수의 기능을 보여줍니다. 값이 (여러) 공백으로 분할되고 다음과 같이 인쇄됩니다.bar,baz,qux,
답변
unset IFS
IFS가 “\ t \ n”으로 추정 되더라도 IFS를 지 웁니다.
$ echo "'$IFS'"
'
'
$ IFS=""
$ echo "'$IFS'"
''
$ unset IFS
$ echo "'$IFS'"
''
$ IFS=$' \t\n'
$ echo "'$IFS'"
'
'
$
bash 버전 4.2.45 및 3.2.25에서 동일한 동작으로 테스트되었습니다.