쉘이 $(의 부분 출력을 처리하는 이유는 무엇입니까?

쉘이 $(의 부분 출력을 처리하는 이유는 무엇입니까?

읽다가 이 줄을 봤는데IFS에 관한 블로그그건:

for i in $(<test.txt)

그리고 $(<test.txt)파일 내용을 STDOUT으로 인쇄하는 것을 생각해 보세요. 제가 틀렸을 수도 있지만 호기심 때문에 쉘에서 이 작업을 시도해 보았습니다. 따라서 array임의의 데이터로 이름이 지정된 임의의 파일이 선택되고

먼저 하나를 수행하면 cat array다음과 같은 결과가 나왔습니다.

amit@C0deDaedalus:~/test$ 
amit@C0deDaedalus:~/test$ cat array
1)      Ottawa  Canada          345644
2)      Kabul   Afghanistan     667345
3)      Paris   France          214423
4)      Moscow  Russia          128793
5)      Delhi   India           142894

그러면 $(<array)나에게 다음이 제공됩니다.

amit@C0deDaedalus:~/test$ $(<array)
1)      Ottawa  Ca: command not found

입력 리디렉션에 사용된다는 것만 알지만 <쉘이 여기에서 이를 명령으로 해석하는 것이 무엇인지 정확히 알지 못합니다.

쉘에서 이 이상한 출력 뒤에 있는 개념을 설명할 수 있는 사람이 있습니까?

고쳐 쓰다:-

실행하면 set -x다음이 제공됩니다.

amit@C0deDaedalus:~/test$ $(<array)
+ '1)' Ottawa Canada 345644 '2)' Kabul Afghanistan 667345 '3)' Paris France 214423 '4)' Moscow Russia 128793 '5)' Delhi India 142894
+ '[' -x /usr/lib/command-not-found ']'
+ /usr/lib/command-not-found -- '1)'
1): command not found
+ return 127
amit@C0deDaedalus:~/test$ 

답변1

$(command)구문은 서브셸 환경에서 실행되며 command자체적으로 and로 대체됩니다 command.Bash 매뉴얼에 따르면, $(< file)단지 더 빠른 것과 같습니다 $(cat file)(그러나 POSIX 기능은 아닙니다).

따라서 를 실행하면 $(<array)Bash는 해당 대체를 수행한 다음 첫 번째 필드를 명령 이름으로 사용하고 나머지 필드를 명령에 대한 인수로 사용합니다.

$ $(<array)
1): command not found

명령/기능 이 없으므로 1)오류 메시지가 인쇄됩니다.

그러나 특정 시나리오에서는 IFS 변수를 수정했기 때문에 다른 오류 메시지가 나타날 수 있습니다.

$ IFS=n; $(<array)
1)      Ottawa  Ca: command not found

편집 1

내 생각엔 당신의 IFS코드가 수정되었기 때문에 쉘이 1) Ottawa Ca대신 실행을 시도하는 것입니다 1). 결국 당신은 IFS관련 기사를 읽고 있는 것입니다. IFS이상한 값이 나와도 놀라지 않을 것입니다.

IFS변수는 소위분사또는필드 분할. 이는 기본적으로 쉘이 확장된 컨텍스트(또는 예를 들어 다른 명령)에서 데이터를 구문 분석하는 방법을 정의합니다 read.

Bash 매뉴얼에서 이 주제를 설명합니다.:

3.5.7 분사

쉘은 단어 분할을 위해 큰따옴표 안에 표시되지 않는 인수 확장, 명령 대체 및 산술 확장의 결과를 스캔합니다.

쉘은 각 문자를 $IFS구분 기호로 처리하고 이러한 문자를 필드 종결자로 사용하여 다른 확장 결과를 단어로 분할합니다. 설정 되지 IFS않거나 해당 값이 기본값인 경우 <space><tab><newline>이전 확장 결과의 시작과 끝 부분에 있는 , 및 의 시퀀스는 무시되고 <space>시작 <tab>이나 끝이 아닌 문자 시퀀스는 단어를 구분하는 데 사용됩니다. 기본값이 아닌 값을 사용하면 공백 문자가 (공백) 값 내에 있는 한 단어의 시작과 끝 부분에 있는 공백 문자 , , 및 시퀀스는 무시됩니다. 공백이 아닌 문자와 인접한 공백 문자는 별도의 필드입니다. 공백 문자 시퀀스도 구분 기호로 간주됩니다. 값이 비어 있으면 단어 분할이 발생하지 않습니다.<newline>IFSIFSspacetabnewlineIFSIFSIFSIFSIFSIFSIFS

명시적 빈 인수( ""또는 '')는 유지되어 빈 문자열로 명령에 전달됩니다. 값이 없는 매개변수 확장으로 인해 암시적으로 인용되지 않은 null 매개변수는 제거됩니다. 값이 없는 인수를 큰따옴표로 확장하면 빈 인수가 생성되며, 이는 유지되어 빈 문자열로 명령에 전달됩니다. null이 아닌 확장된 단어의 일부로 인용된 null 인수가 발생하면 null 인수가 제거됩니다. 즉, 단어 분할 및 빈 매개변수 제거 후에 해당 단어가 됩니다 -d''.-d

확장이 발생하지 않으면 분할이 수행되지 않습니다.

IFS다음은 명령 대체 사용의 몇 가지 예 입니다 .

예시 1:

$ IFS=$' \t\n'; var='hello     world'; printf '[%s]\n' ${var}
[hello]
[world]

$ IFS=$' \t\n'; var='hello     world'; printf '[%s]\n' "${var}"
[hello     world]

두 경우 모두 IFSis <space><tab><newline>(기본값), varis hello world및 명령문이 있습니다 printf. 그러나 첫 번째 경우에는 단어 분리가 수행되지만 두 번째 경우에는 그렇지 않습니다(큰따옴표가 해당 동작을 억제하기 때문).비참조 확장에서는 토큰화가 발생합니다.

예 2:

$ IFS='x'; var='fooxbar'; printf '[%s]\n' ${var}
[foo]
[bar]

$ IFS='2'; (exit 123); printf '[%s]\n' ${?}
[1]
[3]

공백 문자도 없고 공백 문자도 포함되어 있지 ${var}않으므로 ${?}이 경우 단어 분할이 문제가 되지 않을 것이라고 생각할 수도 있습니다. 하지만 이는 남용될 수 있으므로 그렇지 않습니다 IFS.IFS거의 모든 가치를 저장할 수 있으며 쉽게 남용될 수 있습니다.

예시 3:

$ $(echo uname)
Linux

$ $(xxd -p -r <<< 64617465202d75)
Sat Apr 28 12:46:49 UTC 2018

$ var='echo foo; echo bar'; eval "$(echo "${var}")"
foo
bar

이는 토큰화와는 아무 관련이 없지만 코드를 삽입하기 위해 몇 가지 더러운 트릭을 사용하는 방법에 주목하세요.

관련 질문:

관련 정보