Linux Shell Bash 带有特殊含义的退出码
用途说明
exit命令用于退出当前shell,在shell脚本中可以终止当前脚本执行。
常用参数
格式:exit n
退出。设置退出码为n。(Cause the shell to exit with a status of n.)
格式:exit
退出。退出码不变,即为最后一个命令的退出码。(If n is omitted, the exit status is that of the last command executed. )
格式:$?
上一个命令的退出码。
格式:trap "commands" EXIT
退出时执行commands指定的命令。( A trap on EXIT is executed before the shell terminates.)
退出码(exit status,或exit code)的约定:
0表示成功(Zero - Success)
非0表示失败(Non-Zero - Failure)
2表示用法不当(Incorrect Usage)
127表示命令没有找到(Command Not Found)
126表示不是可执行的(Not an executable)
>=128 信号产生
successful or unsuccessful termination, respectively.
以下摘自/usr/include/stdlib.h
- #define EXIT_FAILURE 1 /* Failing exit status. */
- #define EXIT_SUCCESS 0 /* Successful exit status. */
BSD试图对退出码标准化。
以下摘自/usr/include/sysexits.h
- #define EX_OK 0 /* successful termination */
- #define EX__BASE 64 /* base value for error messages */
- #define EX_USAGE 64 /* command line usage error */
- #define EX_DATAERR 65 /* data format error */
- #define EX_NOINPUT 66 /* cannot open input */
- #define EX_NOUSER 67 /* addressee unknown */
- #define EX_NOHOST 68 /* host name unknown */
- #define EX_UNAVAILABLE 69 /* service unavailable */
- #define EX_SOFTWARE 70 /* internal software error */
- #define EX_OSERR 71 /* system error (e.g., can‘t fork) */
- #define EX_OSFILE 72 /* critical OS file missing */
- #define EX_CANTCREAT 73 /* can‘t create (user) output file */
- #define EX_IOERR 74 /* input/output error */
- #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
- #define EX_PROTOCOL 76 /* remote error in protocol */
- #define EX_NOPERM 77 /* permission denied */
- #define EX_CONFIG 78 /* configuration error */
- #define EX__MAX 78 /* maximum listed value */
使用示例
示例一 退出当前shell
[[email protected] ~]#
[[email protected] ~]# exit
logout
示例二 在脚本中,进入脚本所在目录,否则退出
- cd $(dirname $0) || exit 1
示例三 在脚本中,判断参数数量,不匹配就打印使用方式,退出
- if [ "$#" -ne "2" ]; then
- echo "usage: $0 <area> <hours>"
- exit 2
- fi
示例四 在脚本中,退出时删除临时文件
- trap "rm -f tmpfile; echo Bye." EXIT
示例五 检查上一命令的退出码
- ./mycommand.sh
- EXCODE=$?
- if [ "$EXCODE" == "0" ]; then
- echo "O.K"
- fi
表格 D-1. "保留的"退出码
退出码的值 | 含义 | 例子 | 注释 |
---|---|---|---|
1 | 通用错误 | let "var1 = 1/0" | 各种各样的错误都可能使用这个退出码, 比如"除0错误" |
2 | shell内建命令使用错误(Bash文档上有说明) | 很少看到, 通常情况下退出码都为1 | |
126 | 命令调用不能执行 | 程序或命令的权限是不可执行的 | |
127 | "command not found" | 估计是$PATH不对, 或者是拼写错误 | |
128 | exit 的参数错误 | exit 3.14159 | exit只能以整数作为参数, 范围是0 - 255(见脚注) |
128+n | 信号"n"的致命错误 | kill -9 脚本的$PPID | $? 返回137(128 + 9) |
130 | 用Control-C来结束脚本 | Control-C是信号2的致命错误, (130 = 128 + 2, 见上边) | |
255* | 超出范围的退出状态 | exit -1 | exit命令只能够接受范围是0 - 255的整数作为参数 |
通过上面的表, 我们了解到, 退出码1 - 2, 126 - 165, 和255 [1] 都具有特殊的含义, 因此应该避免使用用户指定的退出参数. 如果脚本使用exit 127作为退出语句, 那么可能就会在故障诊断的时候产生混淆(如何判断这是由"command not found"引起的, 还是由用户定义引起的?). 然而, 许多脚本使用exit 1作为通用的返回错误值. 因为退出码1能够表示的错误太多了, 不过这么做, 对于调试来说, 也起不到任何帮助的作用.
其实早就有人对退出状态值进行了系统的分类(请参考/usr/include/sysexits.h), 不过这个文件是为C/C++程序员准备的. 其实shell脚本也需要这样一个类似的标准. 所以本文作者呼吁限制使用用户定义的退出码, 尤其是范围64 - 113(还有0, 表示成功), 这么做, 就可以和C/C++标准保持一致. 这样我们就有了50个可用的退出码, 而且非常便于故障诊断.
本书中所有例子中的用户定义退出码都符合这个标准, 除了那些超出标准范围的例子, 比如例子 9-2.
只有在Bash或sh提示符下, 当shell脚本退出后, 在命令行上使用$?才会得到与上表相一致的结果. 在某些情况下, 运行C-shell或者tcsh可能会给出不同的值.
注意事项
在C/C++中,__FUNCTION__常量记录当前函数的名称。有时候,在日志输出的时候包含这些信息是非常有用的。而在Bash中,同样有这样一个常量FUNCNAME,但是有一点区别是,它是一个数组而非字符串,其中数组的第一个元素为当前函数的名称。
可能初看有点难以理解,为什么FUNCNAME要是一个数组呢?看看下面的例子,你就明白了。
#!/bin/bashfunction test_func(){
echo "Current $FUNCNAME, \\$FUNCNAME => (${FUNCNAME[@]})"
another_func
echo "Current $FUNCNAME, \\$FUNCNAME => (${FUNCNAME[@]})"}function another_func(){
echo "Current $FUNCNAME, \\$FUNCNAME => (${FUNCNAME[@]})"}
echo "Out of function, \\$FUNCNAME => (${FUNCNAME[@]})"
test_func
echo "Out of function, \\$FUNCNAME => (${FUNCNAME[@]})"
执行后的结果为:
Out of function, $FUNCNAME => ()
Current test_func, $FUNCNAME => (test_func main)
Current another_func, $FUNCNAME => (another_func test_func main)
Current test_func, $FUNCNAME => (test_func main)
Out of function, $FUNCNAME => ()
所以,更加准确地说,FUNCNAME是一个数组,但是bash中会将它维护成类似一个堆栈的形式。
与FUNCNAME相似的另外一个比较有用的常量是BASH_SOURCE,同样是一个数组,不过它的第一个元素是当前脚本的名称。
这在source的时候非常有用,因为在被source的脚本中,$0是父脚本的名称,而不是被source的脚本名称。而BASH_SOURCE
就可以派上用场了。
# If the script is sourced by another scriptif [ -n "$BASH_SOURCE" -a "$BASH_SOURCE" != "$0" ]then
do_something
else # Otherwise, run directly in the shell
do_other
fi
唯一遗憾的是,这种做法会让脚本失去一些可移植性,因为不是所有的shell都支持这些常量。