如果任何命令返回非零值,则中止shell脚本?

Jin*_*Kim 405 unix linux bash shell

我有一个调用许多命令的Bash shell脚本.如果任何命令返回非零值,我希望shell脚本自动退出,返回值为1.

如果没有明确检查每个命令的结果,这可能吗?

例如

dosomething1
if [[ $? -ne 0 ]]; then
    exit 1
fi

dosomething2
if [[ $? -ne 0 ]]; then
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)

Vil*_*ari 698

将其添加到脚本的开头:

set -e
Run Code Online (Sandbox Code Playgroud)

如果一个简单的命令以非零退出值退出,这将导致shell立即退出.一个简单的命令是任何命令都不属于if,while或until测试,或者是&&或||的一部分 名单.

有关更多详细信息,请参阅"set"内部命令的bash(1)手册页.

我个人用"set -e"启动几乎所有的shell脚本.当一个脚本在中间出现故障并且打破脚本其余部分的假设时,让脚本顽固地继续下去真的很烦人.

  • 此外,如果脚本以"bash script.sh"运行,则会忽略shebang行上的标志. (47认同)
  • 这样可行,但我喜欢使用"#!/ usr/bin/env bash",因为我经常从/ bin以外的地方运行bash.并且"#!/ usr/bin/env bash -e"不起作用.此外,当我想打开跟踪进行调试时,有一个地方可以修改为读取"set -xe". (35认同)
  • 注意:如果在bash脚本中声明函数,如果要扩展此功能,函数将需要在函数体内设置-e redeclared. (26认同)
  • 此外,如果你的脚本来源,shebang线将是无关紧要的. (8认同)
  • @JinKim在bash 3.2.48中似乎不是这种情况.在脚本中尝试以下内容:`set -e; tf(){false; }; TF; 回声"还在这里".即使在`tf()`的主体内没有`set -e`,执行也会中止.也许你的意思是说'set -e`不是由*subshel​​ls*继承的,这是真的. (4认同)
  • 请记住,由于管道,这可能还不够.看到我添加的答案. (3认同)

leo*_*loy 191

要添加到接受的答案:

请记住,set -e有时是不够的,特别是如果你有管道.

例如,假设您有此脚本

#!/bin/bash
set -e 
./configure  > configure.log
make
Run Code Online (Sandbox Code Playgroud)

...按预期工作:configure中止执行时出错.

明天你会做出一个看似微不足道的变化:

#!/bin/bash
set -e 
./configure  | tee configure.log
make
Run Code Online (Sandbox Code Playgroud)

......现在它不起作用.这里解释这一点,并提供了一种解决方法(仅限Bash):

#!/bin/bash
set -e 
set -o pipefail

./configure  | tee configure.log
make

  • 感谢您解释“pipefail”与“set -o”一起使用的重要性! (5认同)

Zan*_*ynx 79

您的示例中的if语句是不必要的.就这样做:

dosomething1 || exit 1
Run Code Online (Sandbox Code Playgroud)

如果你接受了Ville Laurikari的建议并使用set -e了一些命令,你可能需要使用它:

dosomething || true
Run Code Online (Sandbox Code Playgroud)

|| true将命令管道有一个true即使命令失败,所以返回值-e选项将不会终止该脚本.


fho*_*olo 27

如果你需要在退出时进行清理,你也可以使用'陷阱'和伪信号ERR.这与捕获INT或任何其他信号的方式相同; 如果任何命令以非零值退出,则bash抛出ERR:

# Create the trap with   
#    trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won't:
ps aux | grep blahblahblah
Run Code Online (Sandbox Code Playgroud)

或者,特别是如果你使用"set -e",你可以捕获EXIT; 当脚本因任何原因退出时,您的陷阱将被执行,包括正常结束,中断,由-e选项引起的退出等.


lum*_*ose 12

-eset -e在顶部运行它.

另外看看set -u.

  • 为了节省其他人需要通读`help set`:`-u`将对未设置变量的引用视为错误. (32认同)

Mar*_*gar 11

$?变量很少用到.伪成语command; if [ $? -eq 0 ]; then X; fi应该总是写成if command; then X; fi.

需要的情况$?是需要根据多个值进行检查:

command
case $? in
  (0) X;;
  (1) Y;;
  (2) Z;;
esac
Run Code Online (Sandbox Code Playgroud)

或者当$?需要重复使用或以其他方式操纵时:

if command; then
  echo "command successful" >&2
else
  ret=$?
  echo "command failed with exit code $ret" >&2
  exit $ret
fi
Run Code Online (Sandbox Code Playgroud)

  • 为什么“应始终写为”?我的意思是,为什么“ *应该*”是这样?当命令很长时(考虑使用一打选项调用GCC),则在检查返回状态之前运行命令更具可读性。 (2认同)

Sha*_*ulF 10

出现错误时,以下脚本将打印一条带有失败命令的红色错误消息,然后退出。
将其放在 bash 脚本的顶部:

# BASH error handling:
#   exit on command failure
set -e
#   keep track of the last executed command
trap 'LAST_COMMAND=$CURRENT_COMMAND; CURRENT_COMMAND=$BASH_COMMAND' DEBUG
#   on error: print the failed command
trap 'ERROR_CODE=$?; FAILED_COMMAND=$LAST_COMMAND; tput setaf 1; echo "ERROR: command \"$FAILED_COMMAND\" failed with exit code $ERROR_CODE"; put sgr0;' ERR INT TERM
Run Code Online (Sandbox Code Playgroud)


小智 5

#!/bin/bash -e
Run Code Online (Sandbox Code Playgroud)

应该足够了。