내부 필드 구분 변수에 값을 추가하는 것이 가능하다는 것을 분명히 알고 있습니다. 예를 들어:
$ IFS=blah
$ echo "$IFS"
blah
$
read -r line
또한 이것이 데이터를 stdin
다음과 같은 변수 에 저장한다는 것도 배웠습니다 line
.
$ read -r line <<< blah
$ echo "$line"
blah
$
그러나 명령은 어떻게 변수에 값을 할당합니까? 먼저 stdin
to 변수의 데이터를 저장 line
한 다음 line
to 의 값을 제공합니까 IFS
?
답변1
POSIX 쉘에서는 read
옵션이 없으면 읽을 수 없습니다.철사, 그것은 읽습니다성격단어가 구분된(백슬래시로 계속되는) 행에서 $IFS
백슬래시를 사용하여 구분 기호(또는 연속 행)를 이스케이프할 수 있습니다.
일반적인 구문은 다음과 같습니다.
read word1 word2... remaining_words
read
이스케이프되지 않은 개행 문자(또는 입력 끝)가 발견될 때까지 한 번 에 1바이트씩 stdin을 읽고, 복잡한 규칙에 따라 분할하고, 분할 결과를 $word1
, $word2
... 에 저장합니다.$remaining_words
예를 들어 다음과 같은 입력의 경우:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
기본값을 사용하면 다음이 $IFS
할당 read a b c
됩니다.
$a
⇐foo
$b
⇐bar baz
$c
⇐blah blahwhatever whatever
이제 하나의 매개변수만 전달하면 read line
Still 이 되지 않습니다 read remaining_words
. 백슬래시 처리는 계속 수행되며 IFS 공백 문자²는 여전히 처음과 끝에서 제거됩니다.
이 -r
옵션은 백슬래시 처리를 제거합니다. 따라서 위의 동일한 명령이 -r
대신 할당됩니다.
$a
⇐foo
$b
⇐bar\
$c
⇐baz bl\ah blah\
이제 분할 부분의 경우 두 가지 범주의 문자가 있다는 것을 인식하는 것이 중요합니다 $IFS
. $IFS
) 및 기타 기본값 으로 발생합니다 . 이 두 가지 유형의 역할은 다르게 취급됩니다.
IFS=:
( :
IFS가 아닌) 공백 문자 의 경우 이와 같은 입력은 , 및 (및 일부 구현 추가 항목 , 그 이상은 중요하지 않음) :foo::bar::
로 분할됩니다 . 그리고 공백으로 바꾸면 합계로만 분할됩니다. 즉, 선행 및 후행 시퀀스는 무시되고 해당 시퀀스는 하나의 시퀀스로 처리됩니다. 에서 공백과 공백이 아닌 문자를 결합할 때 추가 규칙이 있습니다. 일부 구현에서는 IFS의 문자(또는)를 두 배로 늘려 특수 처리를 추가/제거할 수 있습니다.""
"foo"
""
bar
""
""
read -a
:
foo
bar
$IFS
IFS=::
IFS=' '
따라서 여기서 이스케이프 처리되지 않은 선행 및 후행 공백 문자를 제거하지 않으려면 IFS에서 해당 IFS 공백 문자를 제거해야 합니다.
공백이 아닌 IFS 문자가 있는 경우에도 입력 줄에 이러한 문자 중 하나만 포함되어 있고 IFS=: read -r word
POSIX 셸(일부 버전이 아님)에 대한 줄의 마지막 문자(비슷한 입력에서와 같이) 인 경우 입력은 단어로 처리됩니다. 왜냐하면 이러한 쉘에서 문자는 다음과 같이 처리되기 때문입니다.foo:
zsh
pdksh
foo
$IFS
터미네이터이므로 은 word
포함되고 foo
포함되지 않습니다 foo:
.
따라서 내장 함수를 사용하여 입력 줄을 읽는 표준 방법은 다음과 read
같습니다.
IFS= read -r line
(대부분의 read
구현에서는 NUL 문자가 지원되지 않으므로 이는 텍스트 줄에만 작동합니다 zsh
.)
명령이 실행되는 동안에만 다른 설정이 이루어지도록 var=value cmd
하려면 구문을 사용하십시오 .IFS
cmd
역사적 기록
이 read
내장 함수는 Bourne 쉘에 의해 도입되었으며 이미 읽을 수 있습니다.성격, 줄이 아닙니다. 최신 POSIX 셸에는 몇 가지 중요한 차이점이 있습니다.
Bourne 쉘은 옵션(Korn 쉘에 의해 도입됨)을 read
지원하지 않으므로 -r
유사한 방식으로 입력을 사전 처리하는 것 외에 백슬래시 처리를 비활성화할 수 있는 방법이 없습니다 sed 's/\\/&&/g'
.
Bourne 쉘에는 두 가지 유형의 문자 개념이 없습니다(이는 ksh에서도 도입되었습니다). Bourne 셸에서 모든 문자는 빈 문자열이 아닌 ksh, 즉 IFS=: read a b c
입력( foo::bar
에 할당된 경우 bar
) 의 IFS 공백 문자와 동일하게 처리됩니다 .$b
Bourne 쉘에서 다음을 사용하십시오.
var=value cmd
cmd
내장된 경우 (예:) read
완료 후에도 var
설정된 상태로 유지됩니다 . Bourne 쉘에서는 확장뿐만 아니라 모든 것을 분할하는 데 사용되기 때문에 이는 특히 중요합니다. 또한 Bourne 쉘에서 공백 문자를 제거하면 더 이상 작동하지 않습니다.value
cmd
$IFS
$IFS
$IFS
"$@"
Bourne 셸에서 복합 명령을 리디렉션하면 해당 명령이 하위 셸에서 실행되므로(초기 버전에서는 유사하거나 read var < file
작동 exec 3< file; read var <&3
하지 않더라도) Bourne 셸에서는 read
터미널의 사용자 입력 이외의 용도로 거의 사용되지 않습니다. (라인 연속 처리가 의미가 있는 경우)
일부 Unices(예: HP/UX, 에도 있음 util-linux
)에는 여전히 line
입력 줄을 읽는 명령이 있습니다(이것은 이전까지는 표준 UNIX 명령이었습니다).단일 UNIX 사양 버전 2).
head -n 1
이는 한 줄 이상 읽지 않도록 한 번에 한 바이트씩 읽는다는 점을 제외하면 기본적으로 동일합니다 . 이러한 시스템에서는 다음을 수행할 수 있습니다.
line=`line`
물론 이는 새로운 프로세스를 생성하고, 명령을 실행하고, 파이프를 통해 출력을 읽는 것을 의미하므로 ksh보다 훨씬 덜 효율적이지만 IFS= read -r line
여전히 훨씬 더 직관적입니다.
1 검색 가능한 입력이지만 일부 구현에서는 블록 읽기로 되돌아간 다음 최적화로 역추적할 수 있습니다. ksh93은 한 단계 더 나아가 읽은 내용을 기억하고 다음 read
호출 에 사용합니다.현재 손상됨
²IFS 공백 문자, 각 POSIX는 로케일로 분류된 문자 이며 ksh88(POSIX 사양의 기반) 및 대부분의 셸에서 [:space:]
발생하는 것처럼 이는 여전히 SPC, TAB 및 NL로 제한됩니다. $IFS
이와 관련하여 제가 찾은 유일한 POSIX 호환 셸은 입니다 yash
. ksh93
( bash
5.0 기준) 다른 공백(예: CR, FF, VT...)도 포함하지만 단일 바이트 공백으로 제한됩니다(예: Solaris)(일부 영역에는 단일 바이트의 잘림 방지 공백이 포함됨)
답변2
이론
여기에는 두 가지 개념이 있습니다.
IFS
는 입력 필드 구분 기호입니다. 이는 읽은 문자열이 의 문자를 기준으로 분할됨을 의미합니다IFS
. 명령줄에서는IFS
일반적으로 공백 문자이므로 명령줄이 공백으로 분할됩니다.- 이와 같은 작업을 수행한다는 것은 " 값을 갖도록
VAR=value command
명령 환경을 수정 "하는 것을 의미합니다. 기본적으로 명령은 값을 갖는 것으로 간주되지만 그 이후에 실행되는 모든 명령은 여전히 이전 값을 갖는 것으로 간주됩니다 . 즉, 해당 명령문에 대해서만 변수가 수정됩니다.VAR
value
command
VAR
value
VAR
이 경우
따라서 를 실행할 때 IFS= read -r line
수행할 작업은 IFS
빈 문자열(분할에 문자가 사용되지 않으므로 분할이 발생하지 않음)을 설정하여 read
전체 줄을 읽고 변수 word 에 할당할 줄로 처리하는 것뿐입니다 line
. 변경 사항은 IFS
해당 명령문에만 영향을 미치므로 후속 명령은 변경 사항의 영향을 받지 않습니다.
참고로
명령은 정확하고 예상대로 작동하지만 IFS
이 경우 설정은 올바르지 않습니다. 어쩌면 1은 아닐지도 몰라필요한. 내장 섹션 bash
에 대한 매뉴얼 페이지에 쓰여진 대로 read
:
표준 입력 [...]에서 한 줄을 읽으면 첫 번째 단어가 첫 번째 이름에 할당되고 두 번째 단어가 두 번째 이름에 할당되는 식입니다.나머지 단어와 중간 구분 기호를성에 할당. 입력 스트림에서 이름보다 적은 단어를 읽는 경우 나머지 이름에는 null 값이 할당됩니다. 의 문자는
IFS
행을 단어로 분할하는 데 사용됩니다. [...]
변수 만 있으므로 line
각 단어가 변수에 할당됩니다.앞뒤 공백 문자가 필요하지 않은 경우1 그냥 쓰고 read -r line
마무리하시면 됩니다.
unset
[1] 기본값 또는 $IFS
기본값이 read
선행/후행 고려 사항 으로 이어지는 방법의 예IFS 공백,당신은 시도 할 수 있습니다:
echo ' where are my spaces? ' | {
unset IFS
read -r line
printf %s\\n "$line"
} | sed -n l
IFS
실행해 보면 설정을 해제하지 않으면 앞뒤 문자가 유지되지 않는다는 것을 알 수 있습니다. 또한 $IFS
스크립트 앞부분을 수정하면 이상한 일이 발생할 수 있습니다.
답변3
이 명령문은 두 부분으로 나누어 읽어야 합니다. 첫 번째 부분은 IFS 변수의 값을 지웁니다. 이는 더 읽기 쉬운 것과 동일하며 IFS=""
, 두 번째 부분은 line
stdin에서 변수를 읽습니다 read -r line
.
이 구문의 특징은 IFS 효과가 일시적이며 명령에만 영향을 미친다는 것입니다 read
.
뭔가 빠진 것이 없다면, 이 특별한 경우에는 무엇을 설정했는지에 관계없이 변수에서 전체 줄을 읽 IFS
더라도 지우기가 효과가 없습니다 . 여러 변수가 지시문에 인수로 전달되는 경우에만 동작이 변경됩니다 .IFS
line
read
편집하다:
-r
로 끝나는 입력은 특별한 처리 없이 허용됩니다 \
. 즉, 백슬래시는 line
여러 줄 입력을 허용하는 연속 문자가 아닌 변수에 포함됩니다.
$ read line; echo "[$line]"
abc\
> def
[abcdef]
$ read -r line; echo "[$line]"
abc\
[abc\]
IFS를 지우면 잠재적인 선행 및 후행 공백이나 탭을 자르기 위해 읽기를 차단하는 부작용이 있습니다. 예를 들면 다음과 같습니다.
$ echo " a b c " | { IFS= read -r line; echo "[$line]" ; }
[ a b c ]
$ echo " a b c " | { read -r line; echo "[$line]" ; }
[a b c]
이 차이점을 지적해준 rici에게 감사드립니다.
답변4
이것은좋은 대답쉘 구문 관점에서 보면:
간단한 명령은선택적 변수 할당 순서그다음 공간 분리단어 및 리디렉션, 제어 연산자에 의해 종료됩니다. 첫 번째 단어는 실행할 명령을 지정하고 인수 0으로 전달됩니다. 나머지 단어는 호출된 명령에 인수로 전달됩니다.
~에서배시 참조 3.7.4:
간단한 명령이나 기능의 환경은 다음을 통해 일시적으로 향상될 수 있습니다.앞에 매개변수 할당을 추가하세요., 셸 매개변수에 설명된 대로.이러한 할당 문은 명령으로 표시되는 환경에만 영향을 미칩니다..
IFS=""
여기 명령을 통해서만 표시됩니다 read
. 즉, 이 행 외에는 IFS
어떤 값도 변경되지 않습니다 .IFS