Q1. 필드 분할.

Q1. 필드 분할.

IFS다음은 이 사이트와 StackOverflow 의 작동 방식을 이해하는 데 도움이 되는 몇 가지 주제입니다 .

하지만 몇 가지 간단한 질문이 있습니다. 나는 그것이 미래의 독자들에게 더 나은 도움이 될 것이라고 생각했기 때문에 같은 기사에서 그들에게 질문하기로 결정했습니다.

Q1. IFS일반적으로 "필드 분할"이라는 맥락에서 논의됩니다. 예필드 분할그리고분사?

질문 2:POSIX 사양설명하다:

IFS 값이 비어 있으면 필드 분할이 수행되지 않습니다.

IFS=null로 설정하는 것과 동일하게 설정 하고 있습니까 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

답변1

  1. 예, 동일합니다.
  2. 예.
  3. bash 및 유사한 쉘에서는 비슷한 작업을 수행할 수 있습니다 IFS=$' \t\n'. 그렇지 않으면 텍스트 제어 코드 삽입을 사용할 수 있습니다 [space] CTRL+V [tab] CTRL+V [enter]. 그러나 이렇게 할 계획이라면 다른 변수를 사용하여 이전 IFS값을 일시적으로 저장한 다음 나중에 복원하는 것이 좋습니다(또는 한 명령에 대해 이전 값을 임시로 덮어쓰는 구문을 사용하는 것 var=foo command).
    • $line첫 번째 코드 조각은 단어 분리를 수행하는 필드 구분 기호가 없기 때문에 읽은 전체 줄을 그대로 표시합니다 . 그러나 많은 쉘이 문자열을 저장하기 위해 cstring을 사용하기 때문에 NUL의 첫 번째 인스턴스로 인해 여전히 조기 종료될 수 있다는 점을 명심하십시오.
    • 두 번째 스니펫은 입력의 정확한 복사본을 넣지 못할 수 있습니다 $line. 예를 들어 연속된 필드 구분 기호가 여러 개 있는 경우 첫 번째 요소의 단일 인스턴스로 만들어집니다. 이는 일반적으로 주변 공백의 손실로 간주됩니다.
    • 세 번째 스니펫은 공백(일반적인 공백, 탭 또는 줄 바꿈이 아님)으로만 분할된다는 점을 제외하면 두 번째와 동일한 작업을 수행합니다.

답변2

질문 1: 그렇습니다. "필드 분할"과 "단어 분할"은 동일한 개념을 나타내는 두 가지 용어입니다.

질문 2: 그렇습니다. 설정하지 않은 경우 IFS(즉, 이후 ) (공백, 탭 및 줄 바꿈) 로 설정하는 unset IFS것과 같습니다 . null 값으로 설정된 경우 (여기서는 "null"이 의미함)(예: 또는 또는 이후 ) 필드 분할이 전혀 수행되지 않습니다(그리고 일반적으로 사용되는 첫 번째 문자 는 공백 문자입니다).IFS$' \t\n'IFSIFS=IFS=''IFS=""$*$IFS

Q3: 기본 동작을 원하면 를 IFS사용할 수 있습니다 unset IFS. 이 기본값을 명시적으로 설정하려면 IFS리터럴 문자 공백, 탭, 개행을 작은따옴표로 묶을 수 있습니다. ksh93, bash 또는 zsh에서는 .portable을 사용할 수 있습니다 IFS=$' \t\n'. 소스 파일에서 리터럴 탭을 피하려면 사용할 수 있습니다.

IFS=" $(echo t | tr t \\t)
"

Q4: IFSnull로 설정하는 경우 종료 개행 문자를 제외한 전체 줄로 read -r line설정하세요 . line를 사용하면 IFS=" "선행 및 후행 공백이 잘립니다. 기본값을 사용하면 IFS탭과 공백이 잘립니다.

답변3

Q1. 필드 분할.

필드 분할은 단어 분할과 동일합니까?

예, 둘 다 같은 생각을 가리킵니다.

Q2: 언제IFS가 비어 있음? .

설정이 IFS=''null과 동일하고 빈 문자열과 동일합니까?

예, 세 가지 모두 동일한 의미입니다. 필드/단어 분할을 수행하면 안 됩니다. 또한 이는 필드 인쇄에 영향을 미치며( 와 동일 echo "$*") 모든 필드는 공백 없이 함께 연결됩니다.

Q3: (파트 a) IFS 설정을 해제합니다.

POSIX 사양에서는아래를 읽어보세요:

IFS가 설정되지 않은 경우 쉘은 IFS 값이 다음과 같이 작동합니다.<스페이스><탭><줄바꿈>.

이는 다음과 정확히 동일합니다.

를 사용하면 unset IFS쉘은 IFS가 기본값인 것처럼 동작합니다.

이는 필드 분할이 기본 IFS 값과 정확히 동일하거나 설정되지 않음을 의미합니다.
이는 IFS가 모든 조건에서 동일한 방식으로 작동한다는 의미는 아닙니다. 보다 구체적으로 실행하면 OldIFS=$IFSvar OldIFS가 다음으로 설정됩니다.유효하지 않은, 기본값이 아닙니다. IFS를 다시 설정해 보세요. 이렇게 하면 IFS=OldIFSIFS를 이전처럼 설정하지 않은 상태로 두는 대신 null로 설정됩니다. 주의 깊은! ! .

Q3: (파트 b) IFS를 복원합니다.

IFS 값을 기본값으로 복원하는 방법. IFS를 기본값으로 복원하고 싶다고 가정해 보겠습니다. 어떻게 해야 하나요? (더 구체적으로 어떻게 참조합니까?<탭>그리고<줄 바꿈>? )

zsh, ksh 및 bash(AFAIK)의 경우 IFS를 기본값으로 설정할 수 있습니다.

IFS=$' \t\n'        # works with zsh, ksh, bash.

다 읽고 나면 다른 내용을 읽을 필요가 없습니다.

그러나 sh에 대해 IFS를 재설정해야 하는 경우 복잡해질 수 있습니다.

(복잡성 제외) 단점이 없는 가장 간단한 방법부터 살펴보겠습니다.

1.- IFS 설정을 해제합니다.

우리는 할 수 있습니다 unset IFS(위의 Q3 부분을 읽으십시오).

2.- 캐릭터를 교환하세요.

해결 방법으로 탭과 줄 바꿈 값을 바꾸면 IFS 값을 더 쉽게 설정할 수 있으며 이는 동등한 방식으로 작동합니다.

IFS를 다음으로 설정<스페이스><줄바꿈><탭>:

sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd'      # Works.

3.- 쉽나요? 해결책:

IFS를 올바르게 설정해야 하는 첨자가 있는 경우 언제든지 수동으로 작성할 수 있습니다.

IFS='   
'

수동으로 입력한 순서는 다음과 같습니다. IFS='spacetabnewline'위의 순서는 실제로 올바르게 입력되었습니다(확인이 필요한 경우 이 답변을 편집하세요). 그러나 브라우저에서 공백을 압축/숨기기 때문에 브라우저에서 복사/붙여넣기 작업이 중단됩니다. 이로 인해 위에 작성된 코드를 공유하기가 어렵습니다.

4.- 완벽한 솔루션.

복사에 안전한 코드를 작성하려면 명시적으로 인쇄 가능한 이스케이프가 필요한 경우가 많습니다.

예상 값을 "생성"하는 코드가 필요합니다. 그러나 개념적으로는 정확하더라도 이 코드는 후행을 설정하지 않습니다 \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.

또 다른 접근 방식은 IFS를 bash(예:)의 환경 값으로 설정한 다음 다음과 같이 sh(환경을 통해 IFS를 설정하는 버전 허용)를 호출하는 것입니다.

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)가 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'

다음 줄은 $avar가 가지고 있는 모든 16진수 값이며, 각 읽기 명령에 주어지기 때문에 끝에 줄 바꿈 "0a"가 있습니다.

빈 IFS가 있는 다음 줄은 "필드 분할"을 수행하지 않지만 개행 문자는 예상대로 제거됩니다.

다음 세 줄의 경우 IFS에 공백이 포함되어 있으므로 초기 공백이 제거되고 var 줄이 나머지 잔액으로 설정됩니다.

마지막 네 줄은 인용되지 않은 변수가 수행하는 작업을 보여줍니다. 값은 (여러) 공백으로 분할되어 다음과 같이 인쇄됩니다.bar,baz,qux,

답변4

unset IFSIFS가 이후에 "\t\n"으로 가정되더라도 IFS를 지웁니다.

$ echo "'$IFS'"
'   
'
$ IFS=""
$ echo "'$IFS'"
''
$ unset IFS
$ echo "'$IFS'"
''
$ IFS=$' \t\n'
$ echo "'$IFS'"
'   
'
$

Bash 버전 4.2.45 및 3.2.25에서 테스트되었으며 동일한 동작을 갖습니다.

관련 정보