详解bash中的退出状态机制
程序的退出状态
当一个程序结束时会向父进程报告自己的退出状态(exitstatus).通过传递int类型的变量给库函数exit或系统调用_exit可以设置当前程序的退出状态,在Linux中,通过WEXITSTATUS返回的退出状态的值域为[0,255]之间的整数.如果传递的值不在这个范围内,内核会自动帮你强转为u_int8_t.通过waitpid库函数可以得到子进程的退出状态,其值存储在参数wstatus的低8位中.
//定义在wait.h中 #defineWEXITSTATUS(status)__WEXITSTATUS(status) //定义在waitstatus.h中 /*IfWIFEXITED(STATUS),thelow-order8bitsofthestatus.*/ #define__WEXITSTATUS(status)(((status)&0xff00)>>8)
下面这个例子展示了如何使用waitpid及相关宏函数获取子进程的退出状态:
#include#include #include #include #include #definePARENT_EXIT10086 #defineCHILD_EXIT-10 intmain() { pid_tpid=fork(); if(pid>0) { intwstatus; //父进程等待子进程执行完毕,用WUNTRACED选项追踪已结束的子进程 pid_tchild_pid=waitpid(pid,&wstatus,WUNTRACED); if(WIFEXITED(wstatus)) printf("Childexitstatus:%d\n",WEXITSTATUS(wstatus)); else perror("Badwaitstatus\n"); //父进程退出 exit(PARENT_EXIT); } elseif(pid==0) { //子进程立即退出,因此需要父进程设置WUNTRACED exit(CHILD_EXIT); } else { //处理fork时出现的错误 perror("fork\n"); exit(EXIT_FAILURE); } }
编译并运行上例可以得到被强转后的状态码,我们使用WIFEXITED判断等待的子进程是否执行成功,然后对执行成功子进程使用WEXITSTATUS获取其退出状态.对程序来说,最终的退出状态就是主进程的退出状态.
>gccecitcode.c;./a.out;echo"Parentexitstatus:$?" Childexitstatus:246#-10强转为uint8 Parentexitstatus:102#10086强转为uint8
在POSIX标准中规定退出状态0代表该程序正常退出,1代表发生错误,其他数字由程序自行规定,因此在glibc的stdlib.h中仅定义了如下宏:
#defineEXIT_FAILURE1/*Failingexitstatus.*/ #defineEXIT_SUCCESS0/*Successfulexitstatus.*/
程序本身一般会在文档中事先约定每种退出状态代表的退出原因(termination),例如在ls的帮助文档中:
>ls--help ...其他内容... Exitstatus:#退出状态 0ifOK,#正常执行 1ifminorproblems#次要问题,例如:无法访问子目录 2ifserioustrouble#严重错误,例如:无法访问命令行参数 ...其他内容...
命令的退出状态
在bash中会记录所执行命令的退出状态,可以通过$?获取最近执行的命令的退出状态.bash自身的退出状态为执行的最后一条命令的退出状态,也就等价于显式指定exit$?.如果没有执行任何命令就退出,则bash的退出状态为0,要注意在bash中用0表示true,用非零表示false.
#用exit显式指定退出状态 >bash >exit98 exit >echo$? 98 #什么也不执行则退出状态为0 >bash exit#Ctrl+D退出 >echo$? 0 #默认为最后一条命令的退出状态 >bash >ecasd ecasd:commandnotfound exit#Ctrl+D退出 >echo$? 127
在bash中对不同种类命令的退出状态作出如下规定:
内置命令:由于内置命令执行时不需要启动额外的子进程,因此需要用返回值模拟退出状态.每个函数都定义了自己的退出状态,例如:内置命令source将脚本文件的最后一个命令的返回状态作为命令的返回状态.bash中所有的内置命令都用退出状态2表示用法错误,例如:选项错误,缺少参数.
>cd-+-#错误的参数 bash:cd:-+:invalidoption cd:usage:cd[-L|[-P[-e]][-@]][dir] >echo$? 2
外部命令:外部命令的退出状态就是使用waitpid得到的子进程的退出状态,如果子进程在执行过程被编号为N的信号所终止,则得到的退出状态就为128+N.
Shell函数:定义shell函数时,函数名与之前已定义的只读函数名相同则退出状态为1,当发生语法错误则退出状态为2.执行shell函数时,函数中最后执行的一条命令的退出状态就是整个函数的退出状态.
#二次定义只读函数报错 >func(){echo;} >readonly-ffunc >func;echo$? 0 >func(){echopoi;} bash:func:readonlyfunction >echo$? 1 #定义函数发生语法错误 >fune(){aa} bash:syntaxerrornearunexpectedtoken'{aa}' >echo$? 2 #函数的退出状态是最后执行的命令的退出状态 >funr(){echo;return6;} >funr;echo$? #echo打印的空行 6#return6是函数中最后执行的命令
表达式:使用((...))或let修饰的表达式的退出状态取决于表达式的值,如果表达式的值为0则退出状态为1;如果表达式的值为非零,则退出状态为0.
>let0+0;echo$? 1#表达式值为零 >((7-5));echo$? 0#表达式值非零
命令列表:用;,&,&&,||连接命令被称为命令列表,其中用&&和||连接的命令使用左关联(leftassociativity)模式执行列表中的命令.整个命令列表的退出状态为最后一条命令的退出状态.此外,$(LISTS)以及流程控制结构如:for,while等的返回状态也是结构中的命令列表的退出状态.
#功能:能ping通baidu.com则输出`baidu.comisup`,否则输出`baidu.comisdown`。 >ping-c1baidu.com&>/dev/null&&echo'baidu.comisup'||echo'baidu.comisdown' baidu.comisdown >echo$? 0#无论是否能ping通,命令列表的退出状态都等于最后一条命令的退出状态
左关联模式被广泛应用于各种语言的逻辑运算符优化中.对于逻辑与运算符&&,以eq1&&eq2为例,只有当两边都为True才会返回True,因此当eq1为False时,eq2不会执行;对于逻辑或运算符||,以eq1||eq2为例,只要两边有一个True就会返回True,因此当eq1为True时,eq2不会执行。
脚本:使用.或source运行脚本文件等同于在当前bash中执行代码块,脚本中最后执行的命令的退出状态就是脚本的退出状态.使用./脚本名或bash脚本名的方式执行脚本文件等同于执行外部命令,脚本的退出状态就是外部命令bash的退出状态.如果脚本中最后执行的命令是exit,那么使用.或source执行该脚本文件在执行结束后会退出当前bash.
后台作业与协作进程:使用不带选项的wait命令可以获得最后一个执行完毕的后台作业的退出状态,如果使用wait-n
>{sleep10;aad;}& [1]558 >wait-n1 [1]+Exit127{sleep10;aad;} >coproc{sleep10;aad;} [1]558 >echo$? 0#这是coproc的执行结果 >jobs [1]+Exit127coprocCOPROC{sleep10;aad;}
管道命令:默认情况下,管道的退出状态取决于管道中最后一条命令的退出状态.如果设置了set-opipefail,那么只有在管道中的全部命令的退出状态为0时,整个管道的退出状态才为0,否则就是最后一个非零的退出状态.在管道前添加!符号可以对整个管道的退出状态取反.bash中的特殊变量$PIPESTATUS以数组的形式存储最近执行的前台管道的退出状态,要注意的是单个命令也会被记录,也就是说${PIPESTATUS[0]}和$?是等价的.
#管道的退出状态是最后一条命令的退出状态 >ps|xxp2>/dev/null|cat;echo$? 0 >set-opipefail >ps|xxp2>/dev/null|cat;echo$? 127#设置了pipefail因此得到最后一个非零退出状态 #管道中每个命令的退出状态被按顺序记录在数组中 >easd2>/dev/null|ls/nou2>/dev/null|more2>/dev/null >echo${PIPESTATUS[@]} 12720 #不带管道符号的单个命令也会被记录 >pingasbasdasd2>/dev/null;echo${PIPESTATUS[0]} 2 >pingasbasdasd2>/dev/null;echo$? 2
参考资料
Exitstatusrange
Bashmanpage
以上就是详解bash中的退出状态机制的详细内容,更多关于bash退出状态的资料请关注毛票票其它相关文章!