我没有太多使用 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大约在同一时间启动,并且都在空闲时接收相同的输入数据,但通常是在数据可用时接收。有些东西可能会引入“不同步”输出,从而翻转线路;例如:

  1. 来自的多路复用数据tee实际上先于另一个进程发送到一个进程,这主要取决于 的实现tee。一个简单的tee实现将read输入一定量的输入,然后write输入两次:一次到标准输出,一次到其参数。这意味着这些目的地之一将首先获取数据。

    然而,管道都是缓冲的。这些缓冲区很可能每个都是 1 行,但它们可能更大,这可能会导致接收命令之一在grep另一个命令 ( head) 接收到任何数据之前看到输出所需的所有内容(即 ped 行)。全部。

  2. 尽管如此,也有可能这些命令之一接收到数据但无法及时对其执行任何操作,然后另一个命令接收更多数据并快速处理它。

    例如,即使一次发送一行数据,如果head不知道如何处理它(或者被内核调度延迟),也可以在有机会之前显示其结果。为了进行演示,请尝试添加延迟:这几乎肯定会首先输出输出。grepheadgrepheadps aux | tee >(sleep 1; head -n1) | grep sysloggrep

$ 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其他一些行。headtee

第二行是否出现的不一致是由时序引入的: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()

将标题行重定向psSTDERR,然后grep重定向到STDOUT,但首先删除grep命令本身,以避免源自grep其自身的“噪音”行:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }

相关内容