在Linux下编程时,退出码是非常讲究的,并不能仅仅用0来表示正常,非0来表示不正常来简单地处理。这几天做了一些实验,记录下来,方便以后查看。
shell以0为真!
有下面一段C语言程序:
#include <stdio.h> #include <stdlib.h> int main(){ exit(1); }
它什么事也不做,只是将1返回给调用者,编译后再写一段shell脚本:
#!/bin/bash
if [ ./a.out
];then
echo “shell treat 1 as true”
else
echo “shell treat 0 as true”
fi
评论指出上面的程序是有问题的,在if中用反引号来执行命令不是判断命令的退出码,而是判断命令的输出。但是结论仍然是正确的,将脚本改成下面的代码即可:
#!/bin/bash ./a.out && echo "shell treat 1 as true" || echo "shell treat 0 as true"
运行结果竟然是“shell treat 0 as true”。大部分程序语言都把0作为假,把非0作为真,但是shell脚本却相反,这是因为Unix下的正经程序都是把0退出码当作程序正确运行的标志,而非0的值则可以指示错误的原因。这也是“cd foo && ls bar”这种命令能运行的原因,如果不知道shell把0当真,这种写法简直完全看不懂。
退出码的范围
可以将退出码分为三种类型:一种是返回0用于指示程序正常运行;一种是应用程序自定义的退出码,这种类型的退出码范围为1~127;还有一种会被解释为程序因为信号而退出,这种类型的退出码范围为129~255(唯一弄不懂的一点是如果使用不存在的参数调用git,例如“git add –foo”,退出码竟然是129!这应该是SIGHUP信号的退出码,而一般其他程序对于未知参数都是返回2或者1。根据邮件列表上的回复,git出现这个原因似乎是纯属偶然)。更详细的退出码范围列表在这,虽然这是shell脚本的,不过还比较通用。 为什么exit(3)的原型的参数是int型,但是退出码的范围只有0~255呢?查看手册可知,exit会对它的参数与0377取并(0377是8进制数,也就是十进制的255),取并后就会只得到最后一个字节的值,所以范围只有0~255。 所以对于下面一段程序:
#include <stdio.h> #include <stdlib.h> int main(){ exit(256); }
编译运行后再使用“echo $?”会得到0而非256。
也就是说父进程只能得到子进程一个字节的返回值,并且这个字节的最高位用于指示程序是否由信号而退出。
但是如果应用程序使用了信号处理函数,那么在处理完清洁工作后需要自己调用exit(3),参数如何决定?信号处理函数的原型为:
typedef void (*sighandler_t)(int);
处理函数的参数用于指示信号的类型,所以在信号处理函数中可以用信号值加上128来告诉父进程自己是由于信号而退出的。