管道,{ 列表;仅适用于某些程序

ast*_*ast 13 shell pipe

需要高级用户对这种不可预测的行为进行解释:

ps -eF | { head -n 1;grep worker; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root       441     2  0     0     0   2 pa?15 ?       00:00:00 [kworker/2:1H]
Run Code Online (Sandbox Code Playgroud)

一切看起来都不错,而

ls -la / | { head -n 1;grep sbin; }
Run Code Online (Sandbox Code Playgroud)

只显示输出 head

...我想过stdout 2>&1,对我来说也不起作用,这很奇怪,有什么解释或建议如何处理吗?

jor*_*anm 9

我对使用进行了一些调查strace,这似乎是由于管道左侧的程序写入终端的方式。ls执行命令时,它会将所有数据写入单个write(). 这会导致head消耗所有标准输入。

另一方面ps是批量写出数据,所以只有第一个write()被 消费head,然后才存在。稍后调用write()将转到新生成的grep进程。

这意味着如果您尝试执行的过程grep没有发生在 first 中write(),它将无法工作,因为grep无法看到所有数据(它看到的数据甚至比减去第一行的数据还要少)。

这是尝试在我的系统上为 pid 1 进行 grep 的示例:

$ ps -eF | { head -n2; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | grep '/lib/systemd/systemd$'
root         1     0  0  1697  3768   2 Oct03 ?        00:00:03 /lib/systemd/systemd
$ ps -eF | { head -n1; grep '/lib/systemd/systemd$'; }
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
Run Code Online (Sandbox Code Playgroud)

你的ps -eF例子只是偶然的。


pet*_*rph 6

这是由 glibc 中的缓冲引起的。如果ls输出在一个内部缓冲区中,因此只传递给head. 对于ps -eF,输出更大,因此一旦head完成,以下grep将获取 的其余部分(但不是全部)输出ps

您可以通过取消缓冲管道来摆脱它 - 例如sed -u(我不确定它不是 GNU 扩展):

$ ls -al / | sed -u "#" | { head -n 1; grep bin; }
total 76
drwxr-xr-x   2 root root  4096 Oct  2 21:52 bin
drwxr-xr-x   2 root root  8192 Oct  3 01:54 sbin
Run Code Online (Sandbox Code Playgroud)