作者:帅哥大香蕉 | 来源:互联网 | 2022-11-30 19:52
考虑以下两个C程序:
#include
int main(void) {
raise(SIGTERM);
}
int main(void) {
return 143;
}
如果我运行任何一个,则$?
bash中的值将为143。但是,通过wait
syscall可以区分它们:
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], 0, NULL) = 11148
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 143}], 0, NULL) = 11214
bash显然使用了这一知识,因为第一个导致将结果Terminated
打印到终端上(奇怪的是,即使我将stdout和stderr都重定向到其他地方,也会发生这种情况),而第二个则没有。如何区分这两种情况与bash脚本?
1> blubase..:
我相信不可能从纯bash / shell获取完整的退出代码。Unix的StackExchange的答案非常全面。
所有shell之间的共同点是,如果进程正常终止,则$?
包含退出代码的最低8位(传递给的数字exit()
)。
不同之处在于该过程由信号终止。在所有情况下,POSIX都需要该数字,该数字将大于128。POSIX没有指定该值是多少。但实际上,在我所知道的所有类似Bourne的外壳中,最低的7位$?
将包含信号编号。但是,n
信号号在哪里
在ash,zsh,pdksh,bash,Bourne shell中$?
是128 + n
。这意味着在这些shell中,如果获得$?
的129
,则您不知道是因为进程退出了exit(129)
还是被信号杀死1
(HUP
在大多数系统上)。但是基本原理是,当shell退出自身时,它们默认情况下会返回最后退出的命令的退出状态。通过确保$?
绝对不大于255,可以保持一致的退出状态:
$ bash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
bash: line 1: 16720 Terminated sh -c "kill \$\$"
8f # 128 + 15
$ bash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
bash: line 1: 16726 Terminated sh -c "kill \$\$"
8f # here that 0x8f is from a exit(143) done by bash. Though it's
# not from a killed process, that does tell us that probably
# something was killed by a SIGTERM
因此,我认为,您需要在bash外部运行命令以捕获退出代码。
通过某种抽象,有人提出了关于unbuffer的类似问题,unbuffer是用tcl编写的小脚本。更准确地说,unbuffer将库libexpect与tcl / tk包装器一起使用。从unbuffer的源中,我提取了相关代码以得出解决方法:
$ bash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
bash: line 1: 16720 Terminated sh -c "kill \$\$"
8f # 128 + 15
$ bash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
bash: line 1: 16726 Terminated sh -c "kill \$\$"
8f # here that 0x8f is from a exit(143) done by bash. Though it's
# not from a killed process, that does tell us that probably
# something was killed by a SIGTERM
如果sleep
正常退出,则返回大约以下行:
18383 exp4 0 0
如果睡眠在退出自身之前被杀死,则上面的脚本将大致返回:
18383 exp4 0 0 CHILDKILLED SIGTERM {software termination signal}
如果脚本以终止exit 143
,则该脚本将大致返回:
18383 exp4 0 143
这些字符串的含义可以从的手册中提取expect
。集成功能wait
正在返回上述返回行。前两个值是pid和expect
进程的名称。第四个是退出状态。如果出现单个信息,则会打印更多信息。第六个值是终止时发送到进程的信号。
等待
通常返回四个整数的列表。第一个整数是等待的进程的pid。第二个整数是相应的生成ID。如果发生操作系统错误,则第三个整数为-1,否则为0。如果第三个整数为0,则第四个整数是生成的进程返回的状态。如果第三个整数为-1,则第四个整数是操作系统设置的errno值。还设置了全局变量errorCode。
附加元素可能会出现在等待返回值的末尾。可选的第五个元素标识一类信息。当前,此元素唯一可能的值是CHILDKILLED,在这种情况下,接下来的两个值是C样式信号名称和简短的文本描述。
这意味着第四个值,如果存在的话,第六个值就是您要查找的值。存储整行并提取信号和退出代码,例如,使用以下代码:
RET=$(expectStat script.sh 1>&1)
# Filter status
EXITVALUE="$(echo "$RET" | cut -d' ' -f4)"
SIGNAL=$(echo "$RET" | cut -d' ' -f6)
#echo "Exit value: $EXITVALUE, Signal: $SIGNAL"
if [ -n "$SIGNAL" ]; then
echo "Likely killed by signal"
else
echo "$EXITVALUE"
fi
总之,此解决方法非常不好。也许,还有另一种工具会带来自己的基于c的工具来获取信号的出现。