我有一个执行许多命令的Shell脚本。如果任何命令以非零退出代码退出,如何使shell脚本退出?
$?
在每个命令之后测试的值。简单方法:将set -e
或#!/bin/bash -e
放在Bash脚本的顶部。
我有一个执行许多命令的Shell脚本。如果任何命令以非零退出代码退出,如何使shell脚本退出?
$?
在每个命令之后测试的值。简单方法:将set -e
或#!/bin/bash -e
放在Bash脚本的顶部。
Answers:
在每个命令之后,都可以在$?
变量中找到退出代码,因此您将获得以下内容:
ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
您需要注意管道命令,因为$?
唯一命令会为您提供管道中最后一个元素的返回代码,因此,在代码中:
ls -al file.ext | sed 's/^/xx: /"
如果文件不存在,则不会返回错误代码(因为sed
管道的一部分实际起作用,返回0)。
该bash
壳实际上提供一种阵列,其可以协助在这种情况下,即是PIPESTATUS
。对于每个管道组件,此数组都有一个元素,您可以像这样分别访问它们${PIPESTATUS[0]}
:
pax> false | true ; echo ${PIPESTATUS[0]}
1
请注意,这是您获得false
命令的结果,而不是整个管道的结果。您还可以按照自己的意愿处理整个列表:
pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1
如果您想从管道中获取最大的错误代码,则可以使用以下方法:
true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc
这PIPESTATUS
依次遍历每个元素,rc
如果它大于先前的rc
值,则将其存储在其中。
ls -al file.ext || exit $?
([[]不可移植)
[[ ]]
在中很容易移植bash
,这就是标记问题的地方:-)奇怪的是,它也无法移植ls
,command.com
所以也不是可移植的,我想这很可能,但这与您的观点相同当下。
PIPESTATUS
(即,${PIPESTATUS[0]}
对于第一个命令,${PIPESTATUS[1]}
第二个命令或${PIPESTATUS[*]}
所有退出状态的列表。)
$?
直接检查。通常,您通常希望使用if ls -al file.ext; then : nothing; else exit $?; fi
类似@MarcH所说的等价的东西,ls -al file.ext || exit $?
但是如果then
or else
子句更复杂,则它更易于维护。
[[ $rc != 0 ]]
将给您一个0: not found
或1: not found
错误。应该将其更改为[ $rc -ne 0 ]
。rc=$?
然后也可以将其删除并使用[ $? -ne 0 ]
。
如果要使用$ ?,则需要在每个命令后进行检查,因为$?每个命令退出后更新。这意味着,如果您执行管道,则只会获取管道中最后一个进程的退出代码。
另一种方法是这样做:
set -e
set -o pipefail
如果将其放在shell脚本的顶部,则bash似乎会为您解决这个问题。如前所述,“ set -e”将导致bash退出并在任何简单命令上出现错误。“ set -o pipefail”也将导致bash退出,并且管道中的任何命令也会出错。
PIPESTATUS
在任何地方使用和检查退出代码要容易得多。
#!/bin/bash -e
是启动Shell脚本的唯一方法。您始终可以使用诸如foo || handle_error $?
需要实际检查出口状态之类的东西。
http://cfaj.freeshell.org/shell/cus-faq-2.html#11
如何获得cmd1
in 的退出代码cmd1|cmd2
首先,请注意,cmd1
退出代码可以为非零值,但仍然并不意味着错误。例如发生在
cmd | head -1
您可能会看到141(或带有ksh93的269)退出状态cmd1
,但这是因为在读取一行后终止cmd
时被SIGPIPE信号中断了
head -1
。
知道管道元素的退出状态
cmd1 | cmd2 | cmd3
一个。使用zsh:
退出代码在pipestatus特殊数组中提供。
cmd1
输入退出代码$pipestatus[1]
,输入cmd3
退出代码
$pipestatus[3]
,因此$?
总是与
$pipestatus[-1]
。
b。用bash:
退出代码在PIPESTATUS
特殊数组中提供。
cmd1
输入退出代码${PIPESTATUS[0]}
,输入cmd3
退出代码
${PIPESTATUS[2]}
,因此$?
总是与
${PIPESTATUS: -1}
。
...
有关更多详细信息,请参见以下链接。
对于bash:
# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;
#
# ... the rest of the script goes here
#
function catch_errors() {
# do whatever on errors
#
#
echo "script aborted, because of errors";
exit 0;
}
在bash中,这很容易,只需将它们与&&绑在一起:
command1 && command2 && command3
您还可以使用嵌套的if构造:
if command1
then
if command2
then
do_something
else
exit
fi
else
exit
fi
if (! command)
如果您希望命令返回非零错误代码,也可以编写。
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
cmd="$@" ;
doLog "DEBUG running cmd or exit: \"$cmd\""
msg=$($cmd 2>&1)
export exit_code=$?
# if occured during the execution exit with error
error_msg="Failed to run the command:
\"$cmd\" with the output:
\"$msg\" !!!"
if [ $exit_code -ne 0 ] ; then
doLog "ERROR $msg"
doLog "FATAL $msg"
doExit "$exit_code" "$error_msg"
else
#if no errors occured just log the message
doLog "DEBUG : cmdoutput : \"$msg\""
doLog "INFO $msg"
fi
}
#eof func doRunCmdOrExit
$*
;使用"$@"
而不是保留空间和通配符。