我如何使用 tee 重定向到 grep

Rqo*_*mey 13 grep bash pipe process-substitution tee

我没有太多使用 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
Run Code Online (Sandbox Code Playgroud)

然而,我第一次运行这个(在 zsh 中)结果是错误的顺序,列标题低于 grep 结果(但这并没有再次发生),所以我尝试交换命令:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
Run Code Online (Sandbox Code Playgroud)

只打印第一行,没有别的!我可以使用 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
Run Code Online (Sandbox Code Playgroud)

更新: 似乎 head 导致整个命令截断(如下面的答案所示)下面的命令现在返回以下内容:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806
Run Code Online (Sandbox Code Playgroud)

mrb*_*mrb 22

$ 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
Run Code Online (Sandbox Code Playgroud)

grephead在大约同一时间开始的命令,都在自己的休闲收到相同的输入数据,但通常作为数据变为可用。有些事情可以引入翻转行的“不同步”输出;例如:

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

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

  2. 尽管如此,也有可能其中一个命令接收到数据但无法及时处理,然后另一个命令接收更多数据并快速处理它。

    例如,即使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
Run Code Online (Sandbox Code Playgroud)

我相信你经常在这里只得到一行,因为head接收第一行输入然后关闭它的标准输入并退出。当tee看到其标准输出已关闭时,它会关闭自己的标准输入(来自 的输出ps)并退出。这可能取决于实现。

实际上,这是唯一的数据ps获得要发送的是第一线(当然,因为head是控制这一点),也许有些其他行之前headtee关闭他们的标准输入描述符。

第二行是否出现不一致是由时序引入的:head关闭stdin,但ps仍在发送数据。这两个事件没有很好地同步,因此包含的行syslog仍然有机会使其成为tee的参数(grep命令)。这与上面的解释类似。

您可以通过使用在关闭标准输入/退出之前等待所有输入的命令来完全避免这个问题。例如,使用awk代替head,它将读取并处理其所有行(即使它们没有输出):

ps aux | tee >(grep syslog) | awk 'NR == 1'
Run Code Online (Sandbox Code Playgroud)

但请注意,这些行仍然可能出现乱序,如上所示,可以通过以下方式证明:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')
Run Code Online (Sandbox Code Playgroud)

希望这不是太多细节,但是有很多同时发生的事物相互作用。单独的进程在没有任何同步的情况下同时运行,因此它们在任何特定运行中的操作可能会有所不同;有时,深入挖掘底层流程以解释原因会有所帮助。