Shell退出状态和test命令(一)
在介绍Shell的if与case前,应该先了解其Shell退出状态和test命令。因为if/case甚至循环while的条件是否成立是根据执行命令的退出状态来确定的。
退出状态
就像C语言函数调用必须要有返回值(除了void情况)一样,Shell执行完每一条命令后都会返回一个整数值作为退出状态(exit status)给调用它的程序。这里的Shell命令包括
- 内置Shell命令
- Shell函数
- 外部命令
Shell约定整数值0表示命令执行成功,其它非0表示失败。
可以使用内置变量$?
查询上一个命令退出状态。
$ ls test.F90
test.F90
$ echo $?
0
$ echo foo
ls: cannot access 'foo': No such file or directory
$ echo $?
2
$ echo $? # 此时查询的是上一个echo命令的退出状态
0
我们可以写一个简单的C程序,main函数返回值10,看程序退出状态是否和函数返回值相同。
// shell_exit_status.c
#include <stdio.h>
int main(){
printf("calling shell_exit_status\n");
return 10;
}
$ gcc -o shell_exit_status shell_exit_status.c
$ ./shell_exit_status
calling shell_exit_status
$ echo $?
10
不出所料,shell_exit_status
程序命令执行退出状态与主函数返回值相同。因为像ls
这些命令本质上就是C程序实现。
我们可以通过exit
命令指定退出状态值给调用者。
$ exit 10 # 执行exit命令会立即退出脚本并将10作为退出状态返回给调用者
退出状态值不会超过255,因为只会返回低8位数值给父进程。
管道命令的退出状态
整个管道命令的退出状态反映的是管道中最后一个命令的退出状态。
可以使用参考资料中提到的例子进行验证。
!/bin/bash
echo "Hello World"
exit $1
以上脚本命名为hello_world.sh,其退出状态使用exit命令,具体退出值由脚本参数指定。
$ hello_world.sh 0
Hello World
$ echo $?
0
$hello_world.sh 5
Hello World
$ echo $?
5
使用管道进行测试:
$ hello_world.sh 5 | grep "Hello World"
Hello World
$ echo $? # grep成功查找到该字符串
0
$ hello_world.sh 5 | grep "Hello World" | grep "Hello Universe"
$ echo $? # 最后一个grep命令未能找到字符串
1
可以看出,管道退出状态实际上是管道中最后一个命令的退出状态。
如果想查询管道中每一个命令的退出状态,可以使用Bash内置环境变量PIPESTATUS
查询,它是一个数组变量,包含管道每一条命令的退出状态,感兴趣可以进一步阅读参考资料内容。
命令的逻辑组合
多个命令的逻辑组合,可以使用shell运算符逻辑或(||
)、且(&&
)、非(!
),其作用与C语言中逻辑运算符类似。
- 逻辑非用法:退出状态取反
$ grep program test.F90 # grep查找成功
program demo
end program
$ echo $?
0
$ ! grep program test.F90
program demo
end program
$ echo $?
$ 1
- 逻辑或用法:测试两种条件中是否有一个结果为真
短路(short-circuit)运算符,如果第一个条件为真,整个结果为真,没有必要判断第二个条件了,shell会停止对第二个条件的评估。
$ cd TMP || echo hello # 如果存在TMP目录,echo命令不会执行
$ echo $?
0
$ cd TMP1 || echo hello # 如果不存在TMP1目录,第二个条件echo会执行
-bash: cd: TMP1: No such file or directory
hello
$ echo $?
0
$ cd TMP1 || ls abc.txt
-bash: cd: TMP1: No such file or directory
ls: cannot access 'abc.txt': No such file or directory
$ echo $?
- 逻辑且用法:测试两种条件是否都为真
短路(short-circuit)运算符,如果第一个条件为假,整个结果为假,没有必要判断第二个条件了,shell会停止对第二个条件的评估。
$ cd TMP1 && echo hello
-bash: cd: TMP1: No such file or directory
$ echo $?
1
参考资料

