iru*_*var 11 shell io-redirection file-descriptors process-substitution
这是我最常做的运行grep
,并wc
在一个文件,而无需扫描两次
<file.txt tee >(grep LITERAL) >(wc -l) >/dev/null
Run Code Online (Sandbox Code Playgroud)
然而,这会产生
EXEC LITERAL
32
Run Code Online (Sandbox Code Playgroud)
有时和
32
EXEC LITERAL
Run Code Online (Sandbox Code Playgroud)
在其他时间。(第一个实例中grep
from的输出先于from的输出,wc
在第二个实例中反之亦然。)
另一方面,使用重定向和文件描述符
{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1
Run Code Online (Sandbox Code Playgroud)
我似乎总是得到
EXEC LITERAL
32
Run Code Online (Sandbox Code Playgroud)
我更喜欢输出顺序是可预测的,但是第二种方法可以保证吗?
同时
<file.txt tee >(grep LITERAL) >(wc -l) >/dev/null
Run Code Online (Sandbox Code Playgroud)
和:
{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1
Run Code Online (Sandbox Code Playgroud)
所有tee
、grep
和wc
都是同时启动的。重要的是最后会发生什么。
wc
仅当在标准输入上看到文件结尾时才会打印结果。在第一种情况下,那就是tee
退出的时候,因为然后tee
将关闭正在读取的fd
管道的另一端(由进程替换启动)。wc
无法保证grep
到那时会读取其所有输入,更不用说写入其输出(假设管道可以容纳相当大量的数据,并且wc
可能会比 更快grep
)
在第二种情况下,wc
当正在读取的管道的所有写入者都关闭了管道的末尾时,将看到文件结束。但在这种情况下,有几个作家。tee
(通过其 fd 打开/dev/fd/3
并通过其 fd 3)并且grep
其fd
3 也向管道打开wc
(尽管它没有任何使用它,更不用说写入它了)。内部{
可能会导致一个额外的子 shell 进程,该进程也将打开fd
3 并等待tee
和grep
。
这意味着只有在退出wc
后才会写入其行号。grep
如果您以正确的方式编写它,即关闭不需要打开的 fd:
{ { <file.txt tee /dev/fd/3 4>&- |
grep LITERAL >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
Run Code Online (Sandbox Code Playgroud)
那么在优化子 shell 进程的 shell 中就无法保证顺序。然而,据我所知,唯一的 shell 是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
Run Code Online (Sandbox Code Playgroud)
使用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
Run Code Online (Sandbox Code Playgroud)