我有一个脚本调用另一个脚本。当子脚本失败时,我希望父脚本也失败。
在子脚本中child_1.sh
,我有这样的内容:
if [ $SOME_BAD_CONDITION ] ; then
echo "Error message...."
exit 1
fi
在父母中,我有这个:
#!/bin/bash
set -e
#...
bash ./child_1.sh
echo "continuing to next step..."
bash ./child_2.sh
bash ./child_3.sh
#...
我已经设置了我的环境,以便这种情况$SOME_BAD_CONDITION
总会发生,并且脚本按预期退出并显示错误消息做打印,但父脚本继续:打印消息“继续...”并开始执行下一个脚本。
我认为set -e
如果存在任何具有非零退出代码的子脚本,将确保我的父脚本失败,但这似乎并没有发生。我不确定我在这里做错了什么......
bash版本:4.2.25
更新:
我尝试回显$?
:
bash ./child_1.sh
echo $?
echo "continuing to next step..."
输出如下所示:
错误信息.... 0 继续下一步...
为什么子进程的退出代码没有进入父进程?
答案:原始代码片段不完整。它exit 1
位于通过管道传送到发球台的代码块内。我尝试发布干净、短代码示例忽略了这一点,因为我没有意识到它有多么重要(我对 bash 脚本还很陌生)。有关详细信息,请参阅我发布的答案。
答案1
因此,我可能犯了一个错误,没有发布有关子脚本的足够详细信息 - 为了这个问题的目的,我简化了它,但可能太多了。该exit
语句被隐藏在一个代码块(在{
和之间}
)内,该代码块通过管道传输到tee
日志记录:
{
#...
if [ $SOME_BAD_CONDITION ] ; then
exit 1
fi
#...
} | tee -a $LOGFILE
echo "script ended at $date">>$LOGFILE
据我了解,我总是从这个子脚本中得到 0 的返回码,因为最后一个脚本echo
返回 0。我做了一些研究并发现$管道状态。我能够使用它来确保当我的脚本尝试以退出代码 1 退出时,它将被传递给父脚本:
{
#...
} | tee -a $LOGFILE
EXIT_CODE=${PIPESTATUS[0]}
echo "script ended at $date">>$LOGFILE
exit $EXIT_CODE
结合约翰的建议,我有一个似乎有效的解决方案。
答案2
由于您的子进程以正确的零/非零退出代码约定退出,并且考虑到您按顺序执行它们,我想说您可以只使用运算&&
符:
#!/bin/bash
./child_1.sh
[[ $? -ne 0 ]] && exit # Exit if non-zero exit code
./child_2.sh
./child_3.sh
./child_4.sh
exit 0
在这里,[[ $? -ne 0 ]] && exit
行为如下:
if [ $? -ne 0 ]; then # If: last exit code is non-zero
exit
fi
如果您在通话之间没有任何事情要做,您甚至可以使用...
#!/bin/bash
./child_1.sh && ./child_2.sh && ./child_3.sh && ./child_4
exit $?
操作员的&&
行为就像一个短路与运算符,零退出代码非常类似于true
布尔值。如果在链中的任何时间,脚本以非零值退出,则该脚本&&
将失败,并且不应调用后续脚本。
答案3
看来您的问题源于您如何在子脚本中抛出错误。
来自 man set “如果一个简单的命令(参见上面的 SHELL GRAMMAR)以非零状态退出。如果失败的命令是紧随 while 或 Until 关键字(测试的一部分)的命令列表的一部分,则 shell 不会退出在 if 语句中,&& 或 || 列表的一部分,或者命令的返回值通过 !"
不要使用 set -e,而是使用 trap 语句并抛出错误。 (如果需要使用条件退出。