bash 当管道中的一个进程失败时退出
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32684119/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Exit when one process in pipe fails
提问by Velkan
The goal was to make a simple unintrusive wrapper that traces stdin and stdout to stderr:
目标是制作一个简单的非侵入式包装器,将 stdin 和 stdout 跟踪到 stderr:
#!/bin/bash
tee /dev/stderr | ./script.sh | tee /dev/stderr
exit ${PIPESTATUS[1]}
Test script script.sh
:
测试脚本script.sh
:
#!/bin/bash
echo asd
sleep 1
exit 4
But when the script exits, it doesn't terminate the wrapper. Possible solution is to end the first tee
from the second command of the pipe:
但是当脚本退出时,它不会终止包装器。可能的解决方案是tee
从管道的第二个命令中结束第一个:
#!/bin/bash
# Second subshell will get the PID of the first one through the pipe.
# It will be able to kill the whole script by killing the first subshell.
# Create a temporary named pipe (it's safe, conflicts will throw an error).
pipe=$(mktemp -u)
if ! mkfifo $pipe; then
echo "ERROR: debug tracing pipe creation failed." >&2
exit 1
fi
# Attach it to file descriptor 3.
exec 3<>$pipe
# Unlink the named pipe.
rm $pipe
(echo $BASHPID >&3; tee /dev/stderr) | (./script.sh; r=$?; kill $(head -n1 <&3); exit $r) | tee /dev/stderr
exit ${PIPESTATUS[1]}
That's a lot of code. Is there another way?
这是很多代码。还有其他方法吗?
采纳答案by kvantour
The main issue at hand here is clearly the pipe. In bash, when executing a command of the form
这里的主要问题显然是管道。在bash 中,当执行形式的命令时
command1 | command2
and command2
dies or terminates, the pipe which receives the output (/dev/stdout
) from command1
becomes broken. The broken pipe, however, does not terminate command1
. This will only happen when it tries to write to the broken pipe, upon which it will exit with sigpipe
. A simple demonstration of this can be seen in this question.
并且command2
死亡或终止,接收输出 ( /dev/stdout
)的管道command1
会损坏。然而,破损的管道并没有终止command1
。这只会在它尝试写入损坏的管道时发生,然后它将退出sigpipe
。在这个问题中可以看到一个简单的演示。
If you want to avoid this problem, you should make use of process substitutionin combination with input redirection. This way, you avoid pipes. The above pipeline is then written as:
如果你想避免这个问题,你应该结合使用进程替换和输入重定向。这样,您就可以避免使用管道。上面的管道然后写成:
command2 < <(command1)
In the case of the OP, this would become:
在 OP 的情况下,这将变成:
./script.sh < <(tee /dev/stderr) | tee /dev/stderr
which can also be written as:
也可以写成:
./script.sh < <(tee /dev/stderr) > >(tee /dev/stderr)
回答by Ewan Mellor
I think that you're looking for the pipefail option. From the bash man page:
我认为您正在寻找 pipefail 选项。从 bash 手册页:
pipefail
If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully. This option is disabled by default.
管道故障
如果设置,则管道的返回值是以非零状态退出的最后(最右侧)命令的值,如果管道中的所有命令成功退出,则为零。默认情况下禁用此选项。
So if you start your wrapper script with
所以如果你开始你的包装脚本
#!/bin/bash
set -e
set -o pipefail
Then the wrapper will exit when any error occurs (set -e
) and will set the status of the pipeline in the way that you want.
然后包装器将在发生任何错误时退出 ( set -e
) 并以您想要的方式设置管道的状态。