我相信从纯 bash/shell 获取完整的退出代码是不可能的。
Unix' StackExchange上的答案很全面。
所有 shell 之间的共同点是,如果进程正常终止,$? 包含退出代码的最低 8 位(传递给 exit() 的数字)。
不同之处在于进程被信号终止的时间。在所有情况下,这是 POSIX 要求的,该数字将大于 128。POSIX 没有指定该值可能是什么。但实际上,在我所知道的所有类似 Bourne 的 shell 中,$? 的最低 7 位将包含信号编号。但是,n 是信号编号,
-
在 ash、zsh、pdksh、bash、Bourne shell 中,$? 是 128 + n。这意味着在那些 shell 中,如果你得到一个 $? 或 129,你不知道是因为进程以 exit(129) 退出还是被信号 1 (@987654334 @ 在大多数系统上)。但基本原理是,当 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 之外运行命令来捕获退出代码。
通过一些抽象,similar question 被问及unbuffer,这是一个用 tcl 编写的小脚本。更准确地说,unbuffer 使用带有 tcl/tk 包装器的库 libexpect。
我从 unbuffer 的源代码中提取了相关代码以得出解决方法:
#!/bin/bash
expectStat() {
expect <(cat << EOT
set stty_init "-opost"
set timeout -1
eval [list spawn -noecho ] $@
expect
send_user "[wait]\n"
EOT
)
}
expectStat sleep 5 &
wait
如果sleep 正常退出,则返回大约以下行:
18383 exp4 0 0
如果 sleep 在它自己退出之前被杀死,上面的脚本将大致返回:
18383 exp4 0 0 CHILDKILLED SIGTERM {software termination signal}
如果脚本以exit 143 终止,则该脚本将大致返回:
18383 exp4 0 143
这些字符串的含义可以从expect的手册中提取。集成函数wait 正在返回上述返回行。
前两个值是 pid 和 expect 的进程名称。
四是退出状态。如果出现信号,则会打印更多信息。第六个值是在进程终止时发送给进程的信号。
等待
通常返回四个整数的列表。第一个整数是等待的进程的 pid。第二个整数是对应的 spawn 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 的工具来获取信号的出现。