管道中的一个进程失败时退出

Vel*_*kan 17 bash

目标是制作一个简单的非侵入式包装器,将stdin和stdout跟踪到stderr:

#!/bin/bash

tee /dev/stderr | ./script.sh | tee /dev/stderr

exit ${PIPESTATUS[1]}
Run Code Online (Sandbox Code Playgroud)

测试脚本script.sh:

#!/bin/bash

echo asd
sleep 1
exit 4
Run Code Online (Sandbox Code Playgroud)

但是当脚本退出时,它不会终止包装器.可能的解决方案是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]}
Run Code Online (Sandbox Code Playgroud)

这是很多代码.还有另外一种方法吗?

Ewa*_*lor 30

我认为你正在寻找pipefail选项.从bash手册页:

pipefail

如果设置,则管道的返回值是以非零状态退出的最后(最右侧)命令的值,如果管道中的所有命令都成功退出,则返回零.默认情况下禁用此选项.

所以如果你用你的包装脚本开始

#!/bin/bash

set -e
set -o pipefail
Run Code Online (Sandbox Code Playgroud)

然后,当发生任何错误(set -e)时,包装器将退出,并将以您想要的方式设置管道的状态.

  • @deFreitas,不是*那个*危险的,也不是所有出乎意料的。不希望 `foo` 在 `foo| 时失败的人 head` 关闭其输出管道并没有认真考虑管道的工作方式。它比 [`set -e`](http://mywiki.wooledge.org/BashFAQ/105) 更可预测。 (4认同)
  • 使用set + o pilefail恢复"默认"行为 (2认同)

kva*_*our 9

此处的主要问题显然是管道。在,执行以下形式的命令时

command1 | command2
Run Code Online (Sandbox Code Playgroud)

command2管芯或终止,其接收输出(管/dev/stdout)从command1变为断开。但是,折断的管道不会终止command1。仅当它尝试写入断开的管道时,才会发生这种情况,并在管道上退出sigpipe。在这个问题中可以简单地证明这一点

如果要避免此问题,则应使用进程替代而不是管道,并将其重写为:

command2 < <(command1)
Run Code Online (Sandbox Code Playgroud)

对于OP,将变为:

./script.sh < <(tee /dev/stderr) | tee /dev/stderr
Run Code Online (Sandbox Code Playgroud)

也可以写成:

./script.sh < <(tee /dev/stderr) > >(tee /dev/stderr)
Run Code Online (Sandbox Code Playgroud)

  • 我找到并理解失败的管道命令背后发生的情况的唯一解释。外星人的符号 `cmd &lt; &lt;() &gt;&gt;()` 就完成了它的工作!在我的例子中,即使使用 bash -eu -o pipefail -c "$CMD" ,`-o pipelinefail` 也不起作用。多谢! (2认同)