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

时间:2015-09-20 20:44:15

标签: bash

目标是制作一个简单的不引人注目的包装器,将stdin和stdout跟踪到stderr:

>>> def outer2(f):
...     def inner2():
...         nonlocal val
...         val+=1
...         print(val)
...         return f()
...     val = 1
...     return inner2
... 
>>> function = lambda : 'Function called'
>>> 
>>> print(outer2(function)())
2
Function called

测试脚本#!/bin/bash tee /dev/stderr | ./script.sh | tee /dev/stderr exit ${PIPESTATUS[1]}

script.sh

但是当脚本退出时,它不会终止包装器。可能的解决方案是从管道的第二个命令结束第一个#!/bin/bash echo asd sleep 1 exit 4

tee

这是很多代码。还有另一种方式吗?

2 个答案:

答案 0 :(得分:23)

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

  

pipefail

     

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

因此,如果您使用

启动包装器脚本
#!/bin/bash

set -e
set -o pipefail

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

答案 1 :(得分:6)

这里的主要问题显然是管道。在中,当执行以下格式的命令时

command1 | command2

并且command2死亡或终止,从/dev/stdout接收输出(command1)的管道断开。但是,断开的管道不会终止command1。仅当它尝试写入断开的管道时,这种情况才会发生,并在管道上以sigpipe退出。在this question中可以看到一个简单的演示。

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

command2 < <(command1)

对于OP,它将变为:

./script.sh < <(tee /dev/stderr) | tee /dev/stderr

也可以写成:

./script.sh < <(tee /dev/stderr) > >(tee /dev/stderr)