"echo 123 >(cat)" 출력에 "/dev/fd/63"이 있는 이유는 무엇입니까?

"echo 123 >(cat)" 출력에 "/dev/fd/63"이 있는 이유는 무엇입니까?
$ echo 123 | cat 
123

내가 예상한 대로 실행 중입니다. 두 명령 모두 동일한 셸 내에서 실행됩니다.

>( ... )그러나 셸에 있는 한 명령의 출력을 하위 셸에 있는 두 번째 명령의 출력에 연결하는 표현식을 사용하여 이를 연결하면 다음과 같은 결과가 나타납니다.

$ echo 123 >(cat)
123 /dev/fd/63

이는 다른 값에서도 마찬가지입니다.

$ echo 101 >(cat)
101 /dev/fd/63

$ echo $BASHPID >(cat)
3252 /dev/fd/63

나는 command1 >(command2)과 같다고 생각했지만 command1 | command2에서는 command1 >(command2)각 명령이 다른 셸 안에 있으므로 동일한 출력을 가져야 합니다. 내가 어디 잘못 됐나요?

답변1

프로세스 대체는 >(thing)파일 이름으로 대체됩니다. 이 파일명은 thing대체 내부 의 표준 입력에 연결되는 파일에 해당합니다 .

다음은 그 사용에 대한 더 나은 예입니다.

$ sort -o >(cat -n >/tmp/out) ~/.profile

이렇게 하면 파일이 정렬 ~/.profile되고 행을 열거 cat -n하고 결과가 /tmp/out.

따라서 질문에 대답하자면 다음과 같습니다. echo두 개의 인수 123와 를 가져오기 때문에 해당 출력을 얻습니다 /dev/fd/63. 프로세스 대체에서 프로세스 /dev/fd/63의 표준 입력에 연결된 파일입니다 .cat

예제 코드를 약간 수정하면 다음과 같습니다.

$ echo 101 > >(cat)

이렇게 하면 표준 출력만 생성됩니다 101( 의 출력은 의 echo입력 역할을 하는 파일로 리디렉션되고 cat해당 cat파일의 내용이 표준 출력에 생성됩니다).


또한 cmd1 | cmd2파이프라인 에서는 (사용 중인 셸 구현에 따라) cmd2동일한 셸에서 전혀 실행되지 않을 수도 있습니다 . 설명하는 방식으로 작동하지만(동일 쉘), 서브 쉘을 생성합니다 ( 쉘 옵션이 설정되어 있고 작업 제어가 활성화되지 않은 경우).cmd1ksh93bashcmd2lastpipe

답변2

완전성을 위해

cmd1 >(cmd2)

대부분 동일하다

cmd1 | cmd2

yash과 그 쉘에만 있습니다.

그 껍질에는>(cmd) 프로세스가 있습니다.리디렉션/ / >(cmd)와 반대로kshbashzsh프로세스인치환.

cmd1 >(cmd2)에서는 yash가 를 기다리지 않기 때문에 엄격히 동일하지는 않습니다. cmd2따라서 다음을 찾을 수 있습니다.

$ yash -c 'echo A >(cat); echo B'
B
A
$ yash -c 'echo A | cat; echo B'
A
B

이에 비해 프로세스치환파일 경로(일반적으로 명명된 파이프 또는 미리 생성된 파이프에 대한 fd /dev/fd/<x>위치 <x>)로 확장되며, 쓰기 위해 열면 출력을 cmd.

프로세스 대체가 에 의해 도입되었지만 ksh해당 셸에서는 이를 리디렉션에 대한 인수로 전달할 수 없습니다.

ksh -c 'cmd1 > >(cmd2)'

프로세스 리디렉션을 에뮬레이션하는 것이 yash작동하지 않습니다. 여기서는 대체 결과로 생성된 파일 이름을 다음과 같은 명령에 인수로 전달해야 합니다.

ksh -c 'diff <(echo a) <(echo b)'

bash이 기능은 및 에서 작동합니다 zsh.

그러나 님 bash의 프로세스 리디렉션과 마찬가지로 yash쉘은 명령( )을 기다리지 않습니다 cmd2. 그래서:

$ bash -c 'echo A > >(cat); echo B'
B
A

ksh프로세스 대체는 다음을 yash사용하여 에뮬레이트할 수 있습니다.

cmd1 /dev/fd/5 5>(cmd2)   

좋다:

diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)

답변3

왜냐하면그게 바로 프로세스 대체가 하는 일이에요을 사용하면 대체 항목 내부의 명령이 파일 이름으로 표시됩니다. 내부적으로는 파이프를 통해 명령을 연결하여 /dev/fd/NN기본 명령에 대한 경로를 제공하므로 이미 열려 있는 파일 설명자를 파이프에 열 수 있습니다.

파이프와는 다릅니다. 파이프는 파일 이름처럼 보이는 어떤 것도 포함하지 않고 stdout연결 됩니다. stdin프로세스 대체는 하나의 명령줄에 이러한 대체를 여러 개 가질 수 있다는 점에서 더 유연하지만 파일을 이름으로 열려면 기본 명령이 필요합니다(예: cat가 아닌 echo).

다음과 같이 프로세스 대체를 사용하여 파이프를 에뮬레이트할 수 있습니다.

echo foo > >(cat -n)

답변4

$ echo 1 >(cat > /dev/null)
1 /dev/fd/63
$ echo echo >(cat /dev/null)
echo /dev/fd/63

# We can trace how the commands are executed
# so long as we avoid using shell builtin commands,
# and run the equivalent external program instead, i.e. /usr/bin/echo

$ strace -f -e execve bash -c '/usr/bin/echo >(cat /dev/null)'
execve("/usr/bin/bash", ["bash", "-c", "/usr/bin/echo >(cat /dev/null)"], [/* 56 vars */]) = 0
strace: Process 4213 attached
[pid  4212] execve("/usr/bin/echo", ["/usr/bin/echo", "/dev/fd/63"], [/* 56 vars */]) = 0
strace: Process 4214 attached
[pid  4214] execve("/usr/bin/cat", ["cat", "/dev/null"], [/* 56 vars */]/dev/fd/63
) = 0
[pid  4212] +++ exited with 0 +++
[pid  4214] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4214, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

# Apparently, the order of evaluation is arranged so this works nicely:

$ echo 1 > >(cat > /dev/null)
$

관련 정보