当一个程序链中的一个程序失败时,如何停止重定向标准输出? [英] How to stop redirecting stdout when one program fails in the chain?
问题描述
在正常情况下,这将输出file.out
In the normal case, this would output file.out
$ program1 | program2 | program3 > file.out
但是,如果 program1
失败怎么办?发生的是,链的其余部分仍然可以触发并且文件已创建
But what happens if program1
fails? What happens is that the rest of the chain still fires and the file is created
$ false | echo worked > file.out
$ cat file.out
worked
我不想显示该文件.
还有另一篇有关此的 SO文章建议添加一个OR子句,如下所示:
There is another SO article about this that suggests adding an OR clause, like so:
$ false | echo worked > file.out || rm file.out
ls file.out
file.out
这不起作用.第二个命令将触发并算作成功.
This doesn't work. The second command fires and counts as a success.
同一篇SO文章还建议使用双&"号,例如:
That same SO article also suggests using a double ampersand, like so:
$ program1 && program2 && program2 > file.out
那并不完全有效.在我的情况下,program2期望从stdout输出,因此该链最终挂起,因为该链没有停止.您可以通过一个简单的示例看到它的工作原理:
That doesn't work entirely. In my case program2 is expecting output from the stdout, so this chain ends up hanging because the chain is not stopping. You can see how this works with a trivial example:
$ echo something > stuff.txt
$ cat stuff.txt && false > file.out
something
$ cat file.out
它没有重定向输出.
但是,更糟糕的是,当事情变得快乐时,它也不起作用.
But, even worse, when things are happy, it doesn't work either.
$ echo something > stuff.txt
$ cat stuff.txt && true > file.out
something
$ cat file.out
在这种情况下,file.out被创建并且为空白.嗯.
In this case, file.out is created and it's blank. Uh-Oh.
我在下面接受了答案.调用 set -o pipefail
是我需要的技巧.在我的实际情况下,我使用的是Makefile.为了使它适用于Makefile,我将其添加到了文件的顶部
I accepted an answer below. Calling set -o pipefail
was the tip that I needed. In my real situation, I'm using a Makefile. To adapt this to work in a Makefile, I added this to the top of the file
SHELL=/bin/bash
然后到达我的目标:
target:
@set -o pipefail; program1 | program2 | program3 > $@
推荐答案
当bash执行 program1 |节目2 |程序3file.out
,它将在启动program1之前创建 file.out
.如果要确保从不创建它,则需要对输出进行缓冲(在内存中或在临时文件中).我发现最干净的语法是这样的:
When bash executes program1 | program2 | program3 > file.out
, it creates file.out
before program1 is started. If you want to ensure that it is never created, you'll need to buffer the output (either in memory or in a temporary file). I find the cleanest syntax for that is something like:
if v=$( set -o pipefail; program1 | program2 | program3 ); then
echo "$v" > file.out
fi
或(这有不同的语义,忽略了返回值.根据您的用例,这可能是可以接受的):
or (this has different semantics, ignoring the return value. Depending on your use case, this may be acceptable):
v=$( program1 | program2 | program3 )
test -n "$v" && echo "$v" > file.out
如果您可以先创建文件然后删除它,则可以
If you're okay with creating the file and then deleting it, you can do
set -o pipefail
program1 | progam2 | program3 > file.out || rm file.out
如果您不想使用pipefail(例如,因为您希望脚本具有可移植性),则可以执行以下操作:
If you don't want to use pipefail (eg, because you want the script to be portable), you can do something like:
{ { { program1 || echo . >&3; } | { program2 || echo . >&3;} |
{ program3 || echo . >&3; } } 3>&1 >&4 |
if grep -q .; then exit 1; else exit 0; fi ; } 4>&1;
这篇关于当一个程序链中的一个程序失败时,如何停止重定向标准输出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!