我希望将两个并发程序(日志文件中的尾部)的输出捕获到bash中的一个输出流中。
我使用这个示例程序进行测试:
function foo { for i in $(seq 1 10); do echo "program $*"; sleep 1; done }
现在以下工作正常
(foo bar & foo baz &) | tee /tmp/output
但是一旦我在混合中添加额外的管道就不再有效了:
(foo bar | grep bar & foo baz &) | tee /tmp/output # does't work
输出变为顺序。我可以制作一个包含grep的单独程序,但我想知道是否有办法解决这个问题。
如果有人能解释为什么它不起作用,我会非常高兴。
答案 0 :(得分:7)
好问题!这个让我感到难过,但我想我知道发生了什么。发生的事情是grep
正在缓冲输出。所以,如果你让它运行,你会看到它最终泛滥。如果你碰巧使用GNU grep
,请尝试传递--line-buffered选项:
(foo bar | grep --line-buffered bar & foo baz &) | tee /tmp/output
为了冒险猜测,并且提醒你基本上是这样,我会说grep
正在缓冲更多输出,因为isatty(1)
表示它不是写到TTY(即使您正在通过tee
观看TTY上的输出)。通过缓冲更多输出,可以减少write()
次呼叫,并且效率更高。运行grep
并观察终端输出的常见行为是行缓冲 - 在找到行时会出现这些行。此选项强制grep
以该模式运行。
请注意,正如手册页警告的那样,这可能会对grep
产生性能影响。
答案 1 :(得分:1)
由于在foo bar
和grep
之间使用了管道,你正在使grep等待foo bar
命令的输出,这就是为什么所有带有bar
的行都来了在巴兹线之后立刻。如果你真的想从命令中grep一些东西,那么定义另一个函数如下:
function foo1 { for i in {1..3}; do echo "program $*" | grep "$*"; sleep 1; done }
然后执行:
(foo1 bar & foo baz &) | tee /tmp/output
现在你会注意到输出如下:
program baz
program bar
program baz
program bar
program baz
program bar
...
...