我没有太多使用 tee 的经验,所以我希望这不是很基础。
查看其中一个答案后这个问题我遇到了一个奇怪的行为tee
。
为了让我输出第一行和找到的行,我可以使用以下命令:
ps aux | tee >(head -n1) | grep syslog
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
然而,我第一次运行这个(在 zsh 中)的结果顺序错误,列标题位于 grep 结果下方(但是这种情况没有再次发生),所以我尝试交换命令:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
只打印第一行,没有其他任何内容!我可以使用 tee 重定向到 grep,还是我这样做的方式错误?
当我输入这个问题时,第二个命令实际上对我有用一次,我再次运行它五次,然后回到一行结果。这只是我的系统吗? (我在 tmux 中运行 zsh)。
最后,为什么第一个命令“grep syslog”没有显示为结果(只有一个结果)?
为了控制这里是 grep 不带tee
ps aux | grep syslog
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
henry 2290 0.0 0.1 95220 3092 ? Ssl Sep07 3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry 15924 0.0 0.0 3128 824 pts/4 S+ 13:44 0:00 grep syslog
更新: 似乎 head 导致整个命令被截断(如下面的答案所示),以下命令现在返回以下内容:
ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806
答案1
$ ps aux | tee >(head -n1) | grep syslog
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
syslog 806 0.0 0.0 34600 824 ? Sl Sep07 0:00 rsyslogd -c4
grep
和命令head
大约在同一时间启动,并且都在空闲时接收相同的输入数据,但通常是在数据可用时接收。有些东西可能会引入“不同步”输出,从而翻转线路;例如:
来自的多路复用数据
tee
实际上先于另一个进程发送到一个进程,这主要取决于 的实现tee
。一个简单的tee
实现将read
输入一定量的输入,然后write
输入两次:一次到标准输出,一次到其参数。这意味着这些目的地之一将首先获取数据。然而,管道都是缓冲的。这些缓冲区很可能每个都是 1 行,但它们可能更大,这可能会导致接收命令之一在
grep
另一个命令 (head
) 接收到任何数据之前看到输出所需的所有内容(即 ped 行)。全部。尽管如此,也有可能这些命令之一接收到数据但无法及时对其执行任何操作,然后另一个命令接收更多数据并快速处理它。
例如,即使一次发送一行数据,如果
head
不知道如何处理它(或者被内核调度延迟),也可以在有机会之前显示其结果。为了进行演示,请尝试添加延迟:这几乎肯定会首先输出输出。grep
head
grep
head
ps aux | tee >(sleep 1; head -n1) | grep syslog
grep
$ ps aux | tee >(grep syslog) | head -n1
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
我相信您通常在这里只得到一行,因为head
接收第一行输入,然后关闭其标准输入并退出。当tee
看到它的 stdout 已关闭时,它会关闭自己的 stdin (来自 的输出ps
)并退出。这可能取决于实现。
实际上,唯一ps
要发送的数据是第一行(当然,因为正在控制这一点),可能还有在其标准输入描述符之前和关闭的head
其他一些行。head
tee
第二行是否出现的不一致是由时序引入的:head
关闭stdin,但ps
仍在发送数据。这两个事件没有很好地同步,因此包含 的行syslog
仍然有机会成为 的tee
参数(grep
命令)。这与上面的解释类似。
您可以通过使用在关闭标准输入/退出之前等待所有输入的命令来完全避免此问题。例如,使用awk
代替head
,它将读取并处理其所有行(即使它们不会导致输出):
ps aux | tee >(grep syslog) | awk 'NR == 1'
但请注意,如上所述,这些行仍然可能出现无序,这可以通过以下方式证明:
ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')
希望这不是太多细节,但是有很多同时发生的事情相互作用。单独的进程同时运行,没有任何同步,因此它们在任何特定运行上的操作可能会有所不同;有时,深入挖掘底层流程以解释原因会有所帮助。
答案2
grep syslog
并不总是显示,因为它取决于时间。使用 shell 管道时,您几乎同时运行命令。但这里的关键是“几乎”二字。如果ps
在启动 grep 之前完成扫描所有进程,则它不会出现在列表中。您可以根据系统负载等获得随机结果。
您的 T 恤也会发生类似的情况。它在子 shell 中的后台运行,并且可以在 grep 之前或之后触发。这就是输出顺序不一致的原因。
至于发球台问题,它的行为很奇怪。这是因为它没有以正常方式使用。它在没有任何参数的情况下运行,这意味着它应该只是将数据从标准输入复制到标准输出。但它的标准输出被重定向到子shell运行头(第一种情况)或grep(第二种情况)。但它也会通过管道传输到下一个命令。我认为这种情况下发生的事情实际上取决于实现。例如,在我的 bash 4.2.28 上,没有任何内容写入子 shell stdin。在 zsh 上,它以您想要的方式可靠地工作(打印 ps 的第一行和搜索到的行),每次我尝试,
答案3
有点 hackish,但这是我的解决方案,采用我使用的 shell 函数的形式psgrep()
:
将标题行重定向ps
到STDERR
,然后grep
重定向到STDOUT
,但首先删除grep
命令本身,以避免源自grep
其自身的“噪音”行:
psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }