tee命令输出到标准输出的顺序是否有保证?

3

您可以在Linux下使用tee命令来拆分管道,方法如下:

printf "line1\nline2\nline3\n" | tee >(wc -l ) | (awk '{print "this is awk: "$0}')

这将产生以下输出:

this is awk: line1
this is awk: line2
this is awk: line3
this is awk: 3

我的问题是,打印顺序是否有保证?tee分割管道计算行数的部分,是否总是在最后打印?有没有一种方法可以始终将其打印在开头?或者说,tee的打印顺序永远不能保证?

3个回答

2

这段代码中没有使用tee定义,但是,正如Daenyth所说,wc命令在tee传递完数据之前不会完成 - 因此通常情况下,tee在将数据传递给awk之后就已经完成了。在这种情况下,让awk来计数可能更好一些。

echo -ne {one,two,three,four}\\n | \
awk '{print "awk processing line " NR ": "$0} END {print "Awk saw " NR " lines"}'

缺点是在完成之前它不会知道数字(要知道数字需要缓冲数据)。在您的示例中,tee和wc的stdout都连接到同一个管道(awk的stdin),但顺序未定义。cat(和大多数其他管道工具)可用于按已知顺序组装文件。
还有更高级的管道技术可以使用,例如bash协处理器(coproc)或命名管道(mkfifo或mknod p)。后者可以为您在文件系统中获取名称,这些名称可以传递给其他进程,但您必须清理它们并避免冲突。tempfile或$$可能对此有用。管道不用于缓冲数据,因为它们通常具有有限的大小,并且将简单地阻止写入。
管道不适用的示例:
mkfifo wcin wcout
wc -l < wcin > wcout &
yes | dd count=1 bs=8M | tee wcin | cat -n wcout - | head

这里的问题是tee会陷入困境,试图将内容写入cat,而cat希望先完成wcout。对于从tee到cat的管道来说,数据太多了。
关于dmckee答案的编辑: 是的,顺序可能是可重复的,但不能保证。这是一个规模、调度和缓冲区大小的问题。在这个GNU/Linux盒子上,示例在几千行后就开始分裂了。
seq -f line%g 20000 | tee >(awk '{print "*" $0 "*"}' ) | \
(awk '{print "this is awk: "$0}') | less
this is awk: line2397
this is awk: line2398
this is awk: line2*line1*
this is awk: *line2*
this is awk: *line3*

哈哈,我想向你请教如何制作命名管道的教程,因为你似乎很懂这方面的知识,但这可能有些过分了 :) - ldog
上面的代码示例出现了问题(如果数据大小小于管道限制,则可以正常工作),它已经使用了命名管道,称为wcin和wcout。wcin从tee连接到wc,wcout从wc连接到cat。请注意,在这种情况下,wc是在其他两者之前启动的-该顺序无关紧要,但它们应该同时运行,因此需要使用&。 - Yann Vernier
非常感谢,尽管您指出命名管道解决方案可能不是一个好的选择,但只要我保持在限制范围内,它就能满足我的需求! - ldog

1

我怀疑在这种情况下,wc正在等待EOF,因此它不会返回(或打印输出),直到第一个命令完成发送输入,而awk逐行操作,因此总是先打印。我不知道在发送到其他进程时是否定义。

为什么不让awk在打印行本身之前计算行数呢?


0

我认为你不能指望它。这里的wc在单独的进程中运行,因此没有同步。我的试验运行表明这可能是可以的(至少在bash中是这样)。正如 Daenyth 解释的那样,这种情况很特殊,但尝试使用grep -o line而不是wc,看看你得到什么。

话虽如此,在我的MacBoox上,我得到:

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(grep -o line ) | (awk '{print "this is awk: "$0}')
this is awk: line1
this is awk: line2
this is awk: line3
this is awk: line4
this is awk: line5
this is awk: line
this is awk: line
this is awk: line
this is awk: line
this is awk: line

非常一致。我必须仔细阅读bash手册才能确定。

同样地:

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(awk '{print "*" $0 "*"}' ) | (awk '{print "this is awk: "$0}')
this is awk: line1
this is awk: line2
this is awk: line3
this is awk: line4
this is awk: line5
this is awk: *line1*
this is awk: *line2*
this is awk: *line3*
this is awk: *line4*
this is awk: *line5*

everytime...and

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(awk '{print "*" $0 "*"}' ) | (grep line)
line1
line2
line3
line4
line5
*line1*
*line2*
*line3*
*line4*
*line5*

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接