带有流程替换的输出顺序

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)

在其他时间。(第一个实例中grepfrom的输出先于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)

我更喜欢输出顺序是可预测的,但是第二种方法可以保证吗?

Sté*_*las 4

同时

<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)

所有teegrepwc都是同时启动的。重要的是最后会发生什么。

wc仅当在标准输入上看到文件结尾时才会打印结果。在第一种情况下,那就是tee退出的时候,因为然后tee将关闭正在读取的fd管道的另一端(由进程替换启动)。wc无法保证grep到那时会读取其所有输入,更不用说写入其输出(假设管道可以容纳相当大量的数据,并且wc可能会比 更快grep

在第二种情况下,wc当正在读取的管道的所有写入者都关闭了管道的末尾时,将看到文件结束。但在这种情况下,有几个作家。tee(通过其 fd 打开/dev/fd/3并通过其 fd 3)并且grepfd3 也向管道打开wc(尽管它没有任何使用它,更不用说写入它了)。内部{可能会导致一个额外的子 shell 进程,该进程也将打开fd3 并等待teegrep

这意味着只有在退出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 上不起作用。

要查看正在运行的进程,您可以替换grepps

$ { { <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)