Unc*_*air 13 bash pipe tail tee
使用bash进程替换,我想同时在文件上运行两个不同的命令.在这个例子中,没有必要,但想象"cat/usr/share/dict/words"是一个非常昂贵的操作,例如解压缩50gb文件.
cat /usr/share/dict/words | tee >(head -1 > h.txt) >(tail -1 > t.txt) > /dev/null
Run Code Online (Sandbox Code Playgroud)
在这个命令之后我希望h.txt包含单词文件"A"的第一行,并且t.txt包含文件"Zyzzogeton"的最后一行.
然而实际发生的是h.txt包含"A"但是t.txt包含"argillaceo",它大约是文件的5%.
为什么会这样?似乎要么"尾巴"进程提前终止,要么流正在混淆.
像这样运行另一个类似命令的行为符合预期:
cat /usr/share/dict/words | tee >(grep ^a > a.txt) >(grep ^z > z.txt) > /dev/null
Run Code Online (Sandbox Code Playgroud)
在这个命令之后,我希望a.txt包含所有以"a"开头的单词,而z.txt包含所有以"z"开头的单词,这正是发生的事情.
那么为什么这不适用于"尾巴",以及其他命令会不起作用?
Eri*_*ouf 11
好吧,似乎发生的事情是,一旦head -1命令完成它退出并导致tee获得一个SIGPIPE它会尝试写入命名管道,生成一个EPIPE和根据的进程替换设置也man 2 write将SIGPIPE在写入过程中生成,这会导致tee退出并强制tail -1立即退出,cat左侧SIGPIPE也是如此.
如果我们向流程中添加更多内容head并使输出更具可预测性并且也可以在stderr不依赖于以下内容的情况下写入,我们可以看到这一点好一点tee:
for i in {1..30}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done") >(tail -1 > t.txt) >/dev/null
Run Code Online (Sandbox Code Playgroud)
当我运行它时给了我输出:
1
Head done
2
Run Code Online (Sandbox Code Playgroud)
因此,在所有内容退出之前,它只有一次循环迭代(尽管t.txt仍然只有1它).如果我们那么做
echo "${PIPESTATUS[@]}"
Run Code Online (Sandbox Code Playgroud)
我们看
141 141
Run Code Online (Sandbox Code Playgroud)
这这个问题关系到SIGPIPE在一个非常类似的方式,我们现在看到的是什么.
coreutils维护者将此作为他们未来后代的tee"陷阱"的一个例子.
有关如何符合POSIX合规性的开发人员的讨论,您可以在http://debbugs.gnu.org/cgi/bugreport.cgi?bug=22195上看到(关闭的notabug)报告.
如果你有GNU版本8.24,他们增加了一些选项(不属于POSIX)访问,可以帮助像-p或--output-error=warn.如果没有它,您可以冒一点风险,但通过捕获和忽略SIGPIPE在问题中获得所需的功能:
trap '' PIPE
for i in {1..30}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done") >(tail -1 > t.txt) >/dev/null
trap - PIPE
Run Code Online (Sandbox Code Playgroud)
将在这两个预期的结果h.txt和t.txt,但如果别的东西发生了通缉SIGPIPE要正确处理你会倒霉的这种方法.
另一个hacky选项是t.txt在开始之前将其清零然后不让head进程列表完成,直到它为非零长度:
> t.txt; for i in {1..10}; do echo "$i"; echo "$i" >&2; sleep 1; done | tee >(head -1 > h.txt; echo "Head done"; while [ ! -s t.txt ]; do sleep 1; done) >(tail -1 > t.txt; date) >/dev/null
Run Code Online (Sandbox Code Playgroud)