Bash가 다음 줄을 정확히 어떻게 처리하는지 이해하려고 노력 중입니다.
$(< "$FILE")
Bash 매뉴얼 페이지에 따르면 이는 다음과 같습니다.
$(cat "$FILE")
두 번째 줄부터 추론의 흐름을 따라갈 수 있습니다. Bash는 입력 명령 대체 시 변수 확장을 수행하여 $FILE
값을 에 전달하고, cat은 Order의 내용을 출력합니다.$FILE
cat
$FILE
그러나 위에서 언급한 첫 번째 줄의 경우 다음과 같이 해석합니다. Bash는 에서 변수 대체를 수행하고 Bash는 표준 입력에서 읽기 위해 $FILE
열립니다 .$FILE
어떻게 든 표준 입력을 표준 출력으로 복사하십시오., 명령 대체가 완료되고 Bash는 생성된 표준 출력을 실행하려고 시도합니다.
$FILE
컨텐츠가 stdin에서 stdout으로 어떻게 이동하는지 설명해 줄 수 있나요 ?
답변1
$(<file)
( ksh93 에서도 `<file`
및 와 함께 사용됨 ) 은 및 에서 복사한 ${<file;}
Korn 셸의 특수 연산자입니다 . 명령 대체와 매우 유사해 보이지만 그렇지 않습니다.zsh
bash
POSIX 셸에서 간단한 명령은 다음과 같습니다.
< file var1=value1 > file2 cmd 2> file3 args 3> file4
모든 부분은 선택 사항이며 리디렉션만, 명령만, 할당만 또는 결합이 가능합니다.
리디렉션이 있지만 명령이 없으면 리디렉션이 수행되지만(따라서 a가 > file
열리고 잘림 file
) 아무 일도 일어나지 않습니다. 그래서
< file
읽기용으로 열리지 file
만 명령이 없으므로 아무 일도 일어나지 않습니다. 그런 다음 file
종료되었습니다. $(< file)
간단한 것이라면명령 대체, 그런 다음 빈 상태로 확장됩니다.
내부에POSIX 사양, in 에서 리디렉션만 포함된 $(script)
경우script
지정되지 않은 결과가 발생함. 이는 Korn 쉘의 특별한 동작을 허용하기 위한 것입니다.
ksh(여기에서 테스트됨 ksh93u+
)에서 스크립트에 하나만 포함된 경우간단한 명령(주석은 전후에 허용되지만) 리디렉션(명령 없음, 할당 없음)만 포함되며 첫 번째 리디렉션이 stdin(fd 0)인 경우 ( <
, <<
또는 <<<
) 리디렉션만 입력하면 다음과 같습니다.
$(< file)
$(0< file)
$(<&3)
($(0>&3)
실제로 이것은 실제로 동일한 연산자이기 때문에)$(< file > foo 2> $(whatever))
하지만:
$(> foo < file)
- ...도 아니다
$(0<> file)
- ...도 아니다
$(< file; sleep 1)
- ...도 아니다
$(< file; < file2)
그 다음에
- 첫 번째 리디렉션을 제외한 모든 리디렉션은 무시됩니다(파싱되어 제거됨).
- file/heredoc/herestring(또는 유사한 것을 사용하는 경우 파일 설명자에서 읽을 수 있는 모든 항목)의 내용에서
<&3
후행 개행 문자를 뺀 내용으로 확장됩니다.
$(cat < file)
그거 빼고는 사용감 비슷해요
- 읽기는 셸이 아닌 내부적으로 수행됩니다.
cat
- 배관이나 추가 프로세스가 필요하지 않습니다.
- 위와 같은 이유로 내부 코드가 서브셸에서 실행되지 않으므로 모든 수정 사항(예:
$(<${file=foo.txt})
또는$(<file$((++n)))
)은 이후에도 유지됩니다. - 읽기 오류(파일을 열거나 파일 설명자를 복사할 때 발생하는 오류는 아님)는 자동으로 무시됩니다.
에서는 파일 입력 리디렉션( 또는 , no , ... ) zsh
이 하나만 있는 경우에만 특수 동작이 트리거된다는 점을 제외하면 동일합니다.<file
0< file
<&3
<<<here
< a < b
그러나 다른 쉘을 시뮬레이션하는 것 외에도, 즉 < file
명령 없이 입력 리디렉션이 하나만 있는 경우, 명령 대체 외부에서 zsh
실행 $READNULLCMD
(기본적으로 호출기)하고 리디렉션이 더 많거나 < file
( <&3
, <<<text
, <a <b
, >file
. <a >b
.. ) $NULLCMD
( cat
기본값)이므로 $(<&3)
해당 특수 연산자로 인식 되지 않더라도 ksh
호출하여 cat
실행한 것처럼 작동 합니다.
그러나 의 내용 ksh
은 다음으로 확장되지만 $(< a < b)
in의 내용은 다음의 내용으로 확장됩니다.a
zsh
a
그리고b
(또는 해당 옵션이 비활성화된 상태에서 b
) 복사되고 비어 있는 상태로 확장됩니다.multios
$(< a > b)
a
b
bash
비슷한 연산자가 있지만 몇 가지 차이점이 있습니다.
댓글은 이전에는 허용되지만 이후에는 허용되지 않습니다.
echo "$( # getting the content of file < file)"
작동하지만:
echo "$(< file # getting the content of file )"
아무것도 부풀려지지 않았습니다.
에서와 같이
zsh
stdin에 대한 파일 리디렉션은 하나만 있지만 a에 대한 폴백은 없으므로 리디렉션 이 수행$READNULLCMD
되지만 확장자는 비어 있습니다.$(<&3)
$(< a < b)
어떤 이유로
bash
호출하지 않는 동안에cat
도 여전히 프로세스를 분기하고 파일 내용을 파이프하므로 다른 셸보다 최적화가 훨씬 덜 됩니다. 실제로 내장된 것과 같습니다$(cat < file)
.cat
cat
위에서 설명한 이유로 인해 변경 사항이 손실됩니다(
$(<${file=foo.txt})
예: 위에서 언급한 경우$file
할당이 손실됨).
에서는 (에도 적용됨 )이 콘텐츠를 읽는 더 효율적인 방법입니다 bash
.IFS= read -rd '' var < file
zsh
텍스트변수에 파일을 넣습니다. 또한 후행 개행 문자를 보존할 수 있다는 장점도 있습니다. 또한 ( $mapfile[file]
모듈에서, 일반 파일에만 해당) 바이너리 파일에서도 작동합니다.zsh
zsh/mapfile
pdksh 기반 변형에는 ksh
ksh93에 비해 몇 가지 변경 사항이 있습니다. 흥미롭게도 mksh
(pdksh 파생 쉘 중 하나) 에서
var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)
여기에 있는 문서의 경우처럼 임시 파일이나 파이프를 사용하지 않고 여기 문서의 내용(후행 개행 없음)이 확장되어 유효한 여러 줄 참조 구문이 되기 때문에 최적화되었습니다.
ksh
, 및 zsh
의 모든 버전 에 대한 이식성을 위해서는 주석을 피하고 변수 수정 사항이 유지될 수도 있고 유지되지 않을 수도 있다는 점을 기억하는 것이 bash
가장 좋습니다 .$(<file)
답변2
bash
내부적으로 이 작업을 수행하기 때문에 $(cat < filename)
. 이것은 bash 기능입니다. bash
작동 방식을 정확히 알기 위해서는 소스 코드를 살펴봐야 할 수도 있습니다 .
다음은 이 기능을 처리하는 함수입니다( bash
소스 코드, 문서 참조 builtins/evalstring.c
).
/* Handle a $( < file ) command substitution. This expands the filename,
returning errors as appropriate, then just cats the file to the standard
output. */
static int
cat_file (r)
REDIRECT *r;
{
char *fn;
int fd, rval;
if (r->instruction != r_input_direction)
return -1;
/* Get the filename. */
if (posixly_correct && !interactive_shell)
disallow_filename_globbing++;
fn = redirection_expand (r->redirectee.filename);
if (posixly_correct && !interactive_shell)
disallow_filename_globbing--;
if (fn == 0)
{
redirection_error (r, AMBIGUOUS_REDIRECT);
return -1;
}
fd = open(fn, O_RDONLY);
if (fd < 0)
{
file_error (fn);
free (fn);
return -1;
}
rval = zcatfd (fd, 1, fn);
free (fn);
close (fd);
return (rval);
}
$(<filename)
주석과 정확하게 동일하지는 않습니다 $(cat filename)
. 파일 이름이 대시로 시작하면 후자가 실패합니다 -
.
$(<filename)
원래 에서 ksh
, bash
에서 에 추가되었습니다 Bash-2.02
.
답변3
다음은 차이점을 보여주고 설명하는 bash 3.2 스니펫입니다.
- strace를 사용하여 프로세스를 추적하고 execve 호출을 표시합니다.
strace -f -e trace=execve
- bash 명령을 실행하여 문자열을 읽
bash -c
거나 읽습니다./bin/cat
- 여기에 맞게 병렬 모드의 출력을 80개 열로 나눕니다.
diff -y -W 80
execve(/bin/cat...)
차이점 오른쪽에 추가 내용이 표시 됩니다 .
$ echo $BASH_VERSION
3.2.25(1)-release
$ echo "hi" >/tmp/f
$ strace -f -e trace=execve /bin/bash -c 'echo $(</tmp/f)' >/tmp/no_cat 2>&1
$ strace -f -e trace=execve /bin/bash -c 'echo $(/bin/cat </tmp/f)' >/tmp/wi_cat 2>&1
$ diff -y -W 80 /tmp/no_cat /tmp/wi_cat
execve("/bin/bash", ["/bin/bash", "-c | execve("/bin/bash", ["/bin/bash", "-c
Process 24253 attached (waiting for p | Process 24256 attached (waiting for p
Process 24253 resumed (parent 24252 r | Process 24256 resumed (parent 24255 r
Process 24253 detached | Process 24257 attached (waiting for p
> Process 24257 resumed (parent 24256 r
> Process 24256 suspended
> [pid 24257] execve("/bin/cat", ["/bin
> Process 24256 resumed
> Process 24257 detached
> [pid 24256] --- SIGCHLD (Child exited
> Process 24256 detached
--- SIGCHLD (Child exited) @ 0 (0) -- --- SIGCHLD (Child exited) @ 0 (0) --
hi hi
답변4
<
직접적인 측면은 아니지만bash 명령 대체. 이는 파이프와 같은 리디렉션 연산자이며 일부 쉘에서는 명령 없이 사용할 수 있습니다(POSIX에서는 이 동작을 지정하지 않습니다).
공간이 더 많으면 더 명확해질 수도 있습니다.
echo $( < $FILE )
이것은효과적으로* 더 많은 POSIX 보안과 동일
echo $( cat $FILE )
...이것도 작동합니다*
echo $( cat < $FILE )
마지막 버전부터 시작해 보겠습니다. 인수 없이 실행 됩니다 cat
. 즉, 표준 입력에서 읽습니다. $FILE
표준 입력으로 리디렉션되므로 해당 내용 <
이 cat
표준 출력에 저장됩니다. 그런 다음 매개변수 $(command)
에 푸시된 출력을 교체합니다 .cat
echo
(POSIX 표준 에서는 bash
아님) <
명령 없이 사용할 수 있습니다. bash
(및 zsh
및 ksh
아님 dash
)은 if로 해석 cat <
하지만 새 하위 프로세스를 호출하지는 않습니다. 이는 쉘에 기본적으로 제공되므로 외부 명령을 직접 실행하는 것보다 빠릅니다 cat
. *그래서 제가 "사실상 똑같다"고 말한 것입니다.