在发生错误时运行Bash命令并告诉我它的退出代码,而不退出。

6

我想在一个bash脚本中运行一个可能失败的命令,将其退出代码存储在一个变量中,并运行一个后续命令,无论该退出代码如何。

我试图避免以下问题:

使用set

set +e  # disable exit on error (it was explicitly enabled earlier)
docker exec $CONTAINER_NAME npm test
test_exit_code=$?  # remember exit code of previous command
set -e  # enable exit on error
echo "copying unit test result file to host"
docker cp $CONTAINER_NAME:/home/test/test-results.xml .
exit $test_exit_code

使用 if

if docker exec $CONTAINER_NAME npm test ; then
    test_exit_code=$?
else
    test_exit_code=$?
fi
echo "copying unit test result file to host"
docker cp $CONTAINER_NAME:/home/test/test-results.xml .
exit $test_exit_code

有没有一种语义简单的方法告诉bash"在发生错误时运行命令,并告诉我它的退出代码"?
我最好的备选方法仍然很令人困惑,需要注释来向后续开发人员解释(只是一个更简洁的if/else语句)。
docker exec $CONTAINER_NAME npm test && test_exit_code=$? || test_exit_code=$?
echo "copying unit test result file to host"
docker cp $CONTAINER_NAME:/home/test/test-results.xml .
exit $test_exit_code

你需要一个友好的 try catch 吗? - JRichardsz
2
bash通常不会在失败时退出,因此您无需采取任何特殊措施。 - Barmar
你必须使用 set -e 使其在失败时退出,但这不是默认设置。 - Barmar
在到达此处之前,已明确设置了 set -e - Val Kornea
1
附带说明:如果您的代码涉及POSIX或Bash脚本,我建议在将其投入生产之前始终运行shellcheck 这个语法检查程序来检查脚本:特别是,它将会发现在shell 变量"$CONTAINER_NAME"周围缺少双引号(但诚然,在此变量中肯定不包含空格或 *,因此这种特定参数扩展在此没有任何问题 :)。 - ErikMD
3个回答

8
我认为您可以使用||运算符?它相当于一个“if-else”命令。
以下内容是否适合您的用例?(如果不适合,请随时评论!)
set -e  # implied in a CI context
exit_status=0
docker exec "$CONTAINER_NAME" npm test || exit_status=$?
docker cp "$CONTAINER_NAME:/home/test/test-results.xml" .
exit "$exit_status"

更简洁地说:
set -e  # implied in a CI context
docker exec "$CONTAINER_NAME" npm test || exit_status=$?
docker cp "$CONTAINER_NAME:/home/test/test-results.xml" .
exit "${exit_status:-0}"

另外,如果您对这个退出状态码不感兴趣,您也可以像这样做:

set -e  # implied in a CI context
docker exec "$CONTAINER_NAME" npm test || :
docker cp "$CONTAINER_NAME:/home/test/test-results.xml" .

想要了解更多有关 || : 的详情,请查看 Unix-&-Linux SE 上的这篇回答:
在 Bash 脚本中哪个更符合习惯: || true 还是 || :


这些是or方法的更清晰的变体。 - Val Kornea

1
非常简单,如果命令执行失败,请保存返回代码:
#!/usr/bin/env sh

# Implied by CI
set -e

# Initialise exit return code
rc=0

# Run command or save its error return code if it fail
docker exec "$CONTAINER_NAME" npm test || rc="$?"

printf '%s\n' "copying unit test result file to host"

# Run other command regardless if first one failed
docker cp "$CONTAINER_NAME:/home/test/test-results.xml" .

# Exit with the return code of the first command
exit "$rc"

嗨Léa!谢谢你的回答,但我相信它和我的等价吧?不过没关系,你可能在我发帖时开始打字了;-) - ErikMD
哦,是的 @ErikMD 确实如此。 - Léa Gris

-1
你可以使用try catch语句来获取退出代码,并使用简单的switch case语句根据错误退出代码运行其他命令:
(
exit 2
#here your command which might fail
)
exit_code=$?

case "$exit_code" in

0)  echo "Success execution"
    #do something
    ;;
1)  echo "Error type 1"
    #do something
    ;;
2)  echo "Error type 2"
    #do something
    ;;
*) echo "Unknown error type: $exit_code"
   ;;
esac

将命令括在括号中并不能防止bash在出现错误时退出(我们在脚本开头使用了set -e)。 - Val Kornea
我使用了这种方法。它很有效。在发布之前,我也尝试过这个片段。set -e不是必需的。 - JRichardsz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接