![Bash에서 IFS=와 IFS=$'\n'의 차이점은 무엇입니까?](https://linux55.com/image/198168/Bash%EC%97%90%EC%84%9C%20IFS%3D%EC%99%80%20IFS%3D%24'%5Cn'%EC%9D%98%20%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80%20%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C%3F.png)
Bash에서 이 세 가지 코드 블록 사이에 차이점이 있습니까?
사용 IFS=
:
#!/usr/bin/env bash
while IFS= read -r item; do
echo "[$item]"
done </dev/stdin
사용 IFS=$'\n'
:
#!/usr/bin/env bash
while IFS=$'\n' read -r item; do
echo "[$item]"
done </dev/stdin
사용 -d $'\n'
:
#!/usr/bin/env bash
while read -rd $'\n' item; do
echo "[$item]"
done </dev/stdin
IFS
두 값과 구분자 옵션에 차이가 있는 경우 -d
어떤 상황에서 차이가 발생하나요?
내 테스트에 따르면 모두 동일해 보입니다.
echo $'one two\nthree\tfour' | test-stdin
# outputs:
# [one two]
# [three four]
답변1
IFS=
및 IFS=$'\n'
동일합니다 read
( read
구분 기호가 기본값에서 변경되지 않았다고 가정). 유일한 차이점은 줄 내 줄 바꿈이 단어를 구분하는지 여부지만 줄 바꿈이 줄 안에 나타나지 않는지 여부입니다.
read
(newline)이 기본 구분 기호이기 read -d $'\n'
때문에 동일합니다 .$'\n'
IFS=
그리고 IFS=$'\n'
필드 분할에 영향을 미칩니다. IFS=
필드 분할은 완전히 꺼지고 IFS=$'\n'
대신 개행 문자로 분할됩니다.
IFS=$'\n'
echo $(echo a; echo b)
# prints "a b" on a single line since $'a\nb' is split at
# the newline and therefore echo receives two arguments "a" and "b"
IFS=
echo $(echo a; echo b)
# prints "a" and "b" on separate lines $'a\nb' is passed
# as a single argument to echo
답변2
위의 우수한 자원과 결합@자일스' 답변,@초로바 논평,게다가다른 질문에 대한 답변. 차이점을 설명하기 위해 다음 코드 예제를 구성했습니다.
IFS
(내부 필드 구분 기호라고도 함)은 인라인 구분 기호를 지정합니다(여러 문자 허용, 순서는 중요하지 않음). 기본값은 입니다 IFS=$' \t\n'
. read
변경 가능한 대상이 여러 개인 경우에만 관련이 있습니다.
read
인수 -d
는 줄 구분 기호를 지정합니다(첫 번째 문자만 허용됨). 기본값은 -d $'\n'
.
이와 같이,
# IFS=, -d $'\n', with tab separated fields, across two lines
echo $'a\tb\tc\nz\tx\ty' | while IFS= read -rd $'\t' a b c; do echo "[$a] [$b] [$c]"; done
# [a] [] []
# [b] [] []
# [c
# z] [] []
# [x] [] []
# IFS=tab, with tab separated fields, across two lines
echo $'a\tb\tc\nz\tx\ty' | while IFS=$'\t' read -r a b c; do echo "[$a] [$b] [$c]"; done
# [a] [b] [c]
# [z] [x] [y]
# IFS=tab, with tab separated fields, across two lines, with only a single variable target
echo $'a\tb\tc\nz\tx\ty' | while IFS=$'\t' read -r a; do echo "[$a]"; done
# [a b c]
# [z x y]
# IFS=tab, with space and tab separated fields, across two lines
echo $'a b\tc\nz\tx y' | while IFS=$'\t' read -r a b c; do echo "[$a] [$b] [$c]"; done
# [a b] [c] []
# [z] [x y] []
# IFS=tab+space, with space and tab separated fields, across two lines
echo $'a b\tc\nz\tx y' | while IFS=$'\t ' read -r a b c; do echo "[$a] [$b] [$c]"; done
# [a] [b] [c]
# [z] [x] [y]
# IFS=newline, -d '', with space and tab separated fields, across two lines
echo $'a b\tc\nz\tx y' | while IFS=$'\n' read -rd '' a b c; do echo "[$a] [$b] [$c]"; done
# outputs nothing, as no delimiter means no lines for inline splitting
# IFS=newline, -d '', with space and tab separated fields, across two lines, with trailing null character
printf 'a b\tc\nz\tx y\0' | while IFS=$'\n' read -rd '' a b c; do echo "[$a] [$b] [$c]"; done
# outputs a single line, with two newline separated fields:
# [a b c] [z x y] []
# IFS=newline, -d $'\0', with space and tab separated fields, across two lines, with trailing null character
printf 'a b\tc\nz\tx y\0' | while IFS=$'\n' read -rd $'\0' a b c; do echo "[$a] [$b] [$c]"; done
# outputs a single line, with two newline separated fields:
# [a b c] [z x y] []
이와 같이,
IFS
"필드"를 "행"으로 분할합니다. 이는 "인라인" 분할기입니다.-d
"라인" 분할, 이는 "라인" 스플리터입니다.IFS
구분된 "필드"의 내용을 사용자 정의하려면 사용자 정의하세요.-d
구분선 "선"의 내용을 사용자 정의하려면 사용자 정의하세요.
유용한 사용 사례는 -d
각 필드를 특정 순서에 따라 개별적으로 읽는 것입니다.
echo $'a b\tc\nz\tx y' | {
read -rd ' ' a
echo "a=[$a]"
read -rd $'\t' b
echo "b=[$b]"
read -rd $'\n' c
echo "c=[$c]"
read -rd $'\t' z
echo "z=[$z]"
read -rd $' ' x
echo "x=[$x]"
read -rd $'\n' y
echo "y=[$y]"
}
# a=[a]
# b=[b]
# c=[c]
# z=[z]
# x=[x]
# y=[y]
이와 같이,
IFS
read
정의는 호출이 여러 변수 대상을 허용하는 경우에만 필요합니다.read
호출이 단일 변수 인수만 허용하는 경우IFS
이는 삭제됩니다. 즉,IFS=
이 경우 꾸미기 기능만 제공됩니다.
@자일스' 답변IFS
문맥 밖에서 다루었습니다 read
.
이러한 사용 사례는 두 개의 파일이 포함된 디렉터리에서 파일 이름을 선택하는 것입니다. 하나는 내부에 공백이 있고 다른 하나는 공백이 없습니다.
cd "$(mktemp -d)" || exit 1
touch 'before-space after-space.txt'
touch 'no-space.txt'
# using arrays
# results in correct fields for selection
mapfile -t list < <(ls -1)
select node in "${list[@]}"; do
echo "via mapfile, [$node]"
break
done
echo
# outputs:
# 1) before-space after-space.txt
# 2) no-space.txt
# #? 1
# via mapfile, [before-space after-space.txt]
# using word splitting with default `IFS`
# results in mangled fields for selection
select node in $(ls -1); do
echo "IFS=default [$node]"
break
done
echo
# outputs:
# 1) before-space
# 2) after-space.txt
# 3) no-space.txt
# #? 1
# IFS=default [before-space]
# using word splitting with `IFS=$'\n'`
# results in the correct fields for selection
IFS=$'\n'
select node in $(ls -1); do
echo "IFS=newline [$node]"
break
done
echo
# outputs:
# 1) before-space after-space.txt
# 2) no-space.txt
# #? 1
# IFS=newline [before-space after-space.txt]
# using word splitting with `IFS=`
# results in a jumbled field for selection
IFS=
select node in $(ls -1); do
echo "IFS= [$node]"
break
done
echo
# outputs:
# 1) before-space after-space.txt
# no-space.txt
# #? 1
# IFS= [before-space after-space.txt
# no-space.txt]