当前位置:  开发笔记 > 编程语言 > 正文

使用bash进程替换和尾部的结果不正确?

如何解决《使用bash进程替换和尾部的结果不正确?》经验,为你挑选了1个好方法。

使用bash进程替换,我想同时在文件上运行两个不同的命令.在这个例子中,没有必要,但想象"cat/usr/share/dict/words"是一个非常昂贵的操作,例如解压缩50gb文件.

cat /usr/share/dict/words | tee >(head -1 > h.txt) >(tail -1 > t.txt) > /dev/null

在这个命令之后我希望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

在这个命令之后,我希望a.txt包含所有以"a"开头的单词,而z.txt包含所有以"z"开头的单词,这正是发生的事情.

那么为什么这不适用于"尾巴",以及其他命令会不起作用?



1> Eric Renouf..:

好吧,似乎发生的事情是,一旦head -1命令完成它退出并导致tee获得一个SIGPIPE它会尝试写入命名管道,生成一个EPIPE和根据的进程替换设置也man 2 writeSIGPIPE在写入过程中生成,这会导致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

当我运行它时给了我输出:

1
Head done
2

因此,在所有内容退出之前,它只有一次循环迭代(尽管t.txt仍然只有1它).如果我们那么做

echo "${PIPESTATUS[@]}"

我们看

141 141

这这个问题关系到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

将在这两个预期的结果h.txtt.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

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有