GNU 时间返回的信号与其打印出来的信号不同 更新:

问题描述

在运行 cronjobs 并使用 check_mk 中的 mk-job 来监控其结果时,我偶然发现了这一点:

重击:

$ /usr/bin/time -f "time_exit: %x" timeout -s SIGKILL 2s sleep 10; echo "shell_exit: $?"
Command terminated by signal 9
time_exit: 0
shell_exit: 137

/usr/bin/time 返回的退出代码与其写入格式化输出退出代码不同:

time_exit != shell_exit

为什么?

但是当使用认的 SIGHUP 信号时,退出代码匹配:

$ /usr/bin/time -f "time_exit: %x" timeout -s SIGHUP 2s sleep 10; echo "shell_exit: $?"
Command exited with non-zero status 124
time_exit: 124
shell_exit: 124

与此同时,如果进程仍在运行,我将使用 timeout -k 10s 2s ...,它将首先发送 SIGHUP,并在 10 秒后发送 SIGKILL。希望 SIGHUP 能妥善阻止。

背景

check_mk 提供 mk-job 来监控作业执行。 mk-job 使用时间来记录执行时间和退出代码

man time

时间命令在程序退出、停止或被信号终止时返回。如果程序正常退出,时间的返回值就是它执行并测量的程序的返回值。否则,返回值为 128 加上导致程序停止或终止的信号数。

man timeout

... 可能需要使用 KILL(9) 信号,因为无法捕捉到该信号,在这种情况下,退出状态为 128+9 而不是 124。

解决方法

GNU time%x 仅在进程正常退出时才有意义,而不是被信号杀死。

[STEP 101] $ /usr/bin/time -f "%%x = %x" bash -c 'exit 2'; echo '$? = '$?
Command exited with non-zero status 2
%x = 2
$? = 2
[STEP 102] $ /usr/bin/time -f "%%x = %x" bash -c 'exit 137'; echo '$? = '$?
Command exited with non-zero status 137
%x = 137
$? = 137
[STEP 103] $ /usr/bin/time -f "%%x = %x" bash -c 'kill -KILL $$'; echo '$? = '$?
Command terminated by signal 9
%x = 0
$? = 137
[STEP 104] $

对于time timeout -s SIGKILL 2s sleep 10timeout137正常退出,它不会被SIGKILL杀死,就像我的bash -c 'exit 137'示例。


更新:

查看时间源code,发现无论进程是否正常退出,%x都在盲目调用WEXITSTATUS()

655             case 'x':           /* Exit status.  */
656               fprintf (fp,"%d",WEXITSTATUS (resp->waitstatus));
657               break;

在 Git master 中它添加了新的 %Tx:

549             case 'T':
550               switch (*++fmt)
551                 {
...
575                 case 'x': /* exit code IF terminated normally */
576                   if (WIFEXITED (resp->waitstatus))
577                     fprintf (fp,WEXITSTATUS (resp->waitstatus));
578                   break;

来自 Git master 的 time --help 输出:

  ...

  %Tt  exit type (normal/signalled)
  %Tx  numeric exit code IF exited normally
  %Tn  numeric signal code IF signalled
  %Ts  signal name IF signalled
  %To  'ok' IF exited normally with code zero

  ...