同时
<file.txt tee >(grep LITERAL) >(wc -l) >/dev/null
和:
{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1
所有的tee
,grep
并且wc
是同时启动。那么重要的是最后会发生什么。
wc
仅当在标准输入上看到文件结尾时才打印结果。在第一种情况下,这是tee
退出的时间,因为这时tee
将fd
在wc
正在读取的管道的另一端将其关闭(由进程替换开始)。无法保证grep
届时将读取其所有输入,更不用说写入其输出了(假设管道可以容纳大量数据,并且wc
可能比更快grep
)
在第二种情况下,wc
当正在读取的管道的所有编写器都已关闭管道末端时,将看到文件结尾。在那种情况下,有几位作家。tee
(通过其fd在/dev/fd/3
fd 3 上打开并通过其fd 3 打开),并且其3 grep
也fd
向管道开放wc
(尽管它没有使用它,更不用说写了)。内部{
可能会导致额外的subshell进程,该进程还将fd
打开3并等待tee
和grep
。
这意味着wc
仅在grep
退出后才写入其行号。
如果您编写了正确的方法,那就是关闭不需要打开的fds:
{ { <file.txt tee /dev/fd/3 4>&- |
grep LITERAL >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
这样一来,在优化子外壳程序的外壳程序中就无法保证顺序。但是,我所知道的唯一外壳是ksh93
但ksh93
对管道使用套接字对,因此/dev/fd/3
至少在Linux上无法使用。
要查看正在运行的进程,可以替换grep
为ps
:
$ { { <file.txt tee /dev/fd/3 4>&- | ps -H >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
PID TTY TIME CMD
8727 pts/5 00:00:00 bash
8815 pts/5 00:00:00 bash
8817 pts/5 00:00:00 tee
8818 pts/5 00:00:00 ps
8816 pts/5 00:00:00 wc
使用bash
,您可以看到额外的shell进程,还可以看到它在fd 3上打开了管道,其中包括:
$ (p=$BASHPID; { { <file.txt tee /dev/fd/3 4>&- | lsof -ag "$p" -d3 >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1)
COMMAND PID PGID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 9843 9842 chazelas 3w FIFO 0,8 0t0 153304 pipe
tee 9845 9842 chazelas 3w FIFO 0,8 0t0 153304 pipe
lsof 9846 9842 chazelas 3r DIR 0,3 0 1 /proc
grep LITERAL >&4 3>&- 4>&-
fd 4似乎既被使用又被关闭?