当子进程以非零退出代码退出时,父脚本将继续

当子进程以非零退出代码退出时,父脚本将继续

我有一个脚本调用另一个脚本。当子脚本失败时,我希望父脚本也失败。

在子脚本中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 语句并抛出错误。 (如果需要使用条件退出。

http://mywiki.wooledge.org/BashFAQ/105

相关内容