考虑以下:
command1 | command2 | command3
据我了解,无论可能发生任何错误,管道都会运行每个命令。当命令返回 stderr 时,它不会通过管道传递到下一个命令,但下一个命令仍会运行(除非您使用|&
)。我想要任何可能发生的错误来终止管道的其余部分。我认为set -o pipefail
可以实现这一点,但如果管道中的任何内容失败,它只是终止管道之后可能出现的任何内容,即:
(set -o pipefail; cmd1 | cmd2 && echo "I won't run if any of the previous commands fail")
那么,最简洁的方式终止休息如果任何命令失败,管道会发生什么情况?我还需要它以失败命令的正确 stderr 退出。我是从命令行上下文而不是 shell 脚本执行此操作,因此我寻求简洁性。想法?
答案1
我相信这是不可能的
我相信,由于管道的执行方式,您所要求的并不是直接可能的。当 shell 在管道中执行“稍后”命令时,它不知道命令的成功或失败(返回值)。它实际上是同时运行所有这些,然后收集结果。
有一些解决方法可能会有所帮助。
解决方法1
一次执行一个命令并缓存结果 这更好,因为如果先前的命令失败,后面的命令绝对不会运行。
一个非常短的脚本示例:
cache_file=`tempfile`
if command1 > $cache_file ; then
command2 < $cache_file
fi
rm $cache_file
解决方法2
执行一切但检查返回结果 无论如何,这仍然会运行所有命令,但它确实可以让您回过头来查找原因。
这里每个命令的 STDERR 都被重定向到不同的文件,扩展名为2>
.然后PIPESTATUS
检查以查找每个命令的返回代码。
command1 2> command1_err.log | command2 2> command2_err.log
for result in ${PIPESTATUS[@]} ; do
if [ $result -ne 0 ] ; then
echo command failed
fi
done
在 shell 中运行管道的简要概述
要创建管道,shell 遵循以下步骤如同这些:
答案2
停止管道的一种巧妙方法是中途杀死 shell。现在,这意味着终止您的交互式 shell(如果您从终端模拟器运行它),因此您必须启动另一个 shell 才能继续您的工作。但这至少会停止以下管道命令。
command1 2> command1_error.log | awk -v status=$? -v pid=$$ '{if (status != 0) { system("kill -9 " pid) } else { print } }' | command2 ..
注意:如果您从终端/tty 运行它,这将使您注销
或者,如果你想阻止最后一个命令(例如创建文件的命令是管道中的最后一个命令),你可以使用xargs -r
command1 | command2 | xargs -r command3
如果前一个命令没有输出任何内容,该-r
标志将阻止运行 command3。xargs