需要高级用户对这种不可预测的行为进行解释:
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
,对我来说也不起作用,这很奇怪,有什么解释或建议如何处理吗?
我对使用进行了一些调查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
例子只是偶然的。
这是由 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)