详解bash中的退出状态机制

2020-04-07 14:01:09王冬梅

# 用 exit 显式指定退出状态
> bash
> exit 98
exit
> echo $?
98

# 什么也不执行则退出状态为 0
> bash
exit # Ctrl + D 退出
> echo $?
0

# 默认为最后一条命令的退出状态
> bash
> ecasd
ecasd: command not found
exit # Ctrl + D 退出
> echo $?
127

在 bash 中对不同种类命令的退出状态作出如下规定:

内置命令: 由于内置命令执行时不需要启动额外的子进程, 因此需要用返回值模拟退出状态. 每个函数都定义了自己的退出状态, 例如: 内置命令 source 将脚本文件的最后一个命令的返回状态作为命令的返回状态. bash 中所有的内置命令都用退出状态 2 表示用法错误, 例如: 选项错误, 缺少参数.

> cd -+- # 错误的参数
bash: cd: -+: invalid option
cd: usage: cd [-L|[-P [-e]] [-@]] [dir]
> echo $?
2

外部命令: 外部命令的退出状态就是使用 waitpid 得到的子进程的退出状态, 如果子进程在执行过程被编号为 N 的信号所终止, 则得到的退出状态就为 128+N .

Shell 函数: 定义 shell 函数时, 函数名与之前已定义的只读函数名相同则退出状态为 1 , 当发生语法错误则退出状态为 2 . 执行 shell 函数时, 函数中最后执行的一条命令的退出状态就是整个函数的退出状态.

# 二次定义只读函数报错
> func () { echo; }
> readonly -f func
> func; echo $?
0
> func () { echo poi; }
bash: func: readonly function
> echo $?
1

# 定义函数发生语法错误
> fune () {aa}
bash: syntax error near unexpected token '{aa}'
> echo $?
2

# 函数的退出状态是最后执行的命令的退出状态
> funr () { echo; return 6; }
> funr; echo $?
  # echo 打印的空行
6 # return 6 是函数中最后执行的命令

表达式: 使用 ((...)) 或 let 修饰的表达式的退出状态取决于表达式的值, 如果表达式的值为 0 则退出状态为 1 ; 如果表达式的值为非零, 则退出状态为 0 .

> let 0+0; echo $?
1 # 表达式值为零
> ((7-5)); echo $?
0 # 表达式值非零

命令列表: 用 ; , & , && , || 连接命令被称为命令列表, 其中用 && 和 || 连接的命令使用左关联( left associativity )模式执行列表中的命令. 整个命令列表的退出状态为最后一条命令的退出状态. 此外, $( LISTS ) 以及流程控制结构如: for , while 等的返回状态也是结构中的命令列表的退出状态.

# 功能: 能ping通baidu.com则输出 `baidu.com is up` , 否则输出 `baidu.com is down` 。
> ping -c1 baidu.com &> /dev/null && echo 'baidu.com is up' || echo 'baidu.com is down'
baidu.com is down
> echo $?
0 # 无论是否能 ping 通, 命令列表的退出状态都等于最后一条命令的退出状态