$ echo 123 | cat
123
正在做我期望的事情,兩個命令都在同一個 shell 中運行。
但是,當我使用表達式將它們連接起來>( ... )
(該表達式將 shell 中一個命令的輸出連接到子 shell 中的第二個命令)時,我得到以下結果:
$ 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)
,每個命令都位於不同的 shell 內,因此它們應該具有相同的輸出。我哪裡錯了?
答案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
可能根本不會運行在同一個 shell 中cmd1
(取決於您正在使用的 shell 實作)。 ksh93
按照您描述的方式運作(相同的 shell),同時bash
建立一個子 shell cmd2
(除非lastpipe
設定了其 shell 選項並且作業控制未啟動)。
答案2
為了完整性
cmd1 >(cmd2)
大部分是相同的
cmd1 | cmd2
在yash
外殼中,並且僅在外殼中。
在那個外殼中,>(cmd)
就是進程重定向>(cmd)
與of ksh
//相對,bash
後者zsh
是 process代換。
它並不嚴格等效,因為 in 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
, 在該 shell 中引入的,但您不能將它們作為參數傳遞給重定向。
ksh -c 'cmd1 > >(cmd2)'
模擬yash
進程重定向將不起作用。在那裡,您應該將替換產生的檔案名稱作為參數傳遞給命令,如下所示:
ksh -c 'diff <(echo a) <(echo b)'
它將在bash
和中工作zsh
。
然而,bash
就像 for 的進程重定向一樣yash
,shell 不會等待指令 ( 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)
$