為什麼「echo 123 >(cat)」的輸出中有「/dev/fd/63」?

為什麼「echo 123 >(cat)」的輸出中有「/dev/fd/63」?
$ 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主命令的路徑,因此它可以打開管道中已經打開的檔案描述符。

它與管道不一樣。管道連接stdoutstdin涉及任何看起來像檔案名稱的內容。進程替換更加靈活,因為您可以在一個命令列中進行多個此類替換,但它需要主命令按名稱開啟檔案(例如,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)
$

相關內容