为什么 bash 在终止进程后“并不总是”显示“已终止”消息?

为什么 bash 在终止进程后“并不总是”显示“已终止”消息?

为什么这种差异很重要?下面代码中的两个块的不同之处在于最后一行:

#!/bin/bash
if [[ -n "$1" ]]; then
    sleep 1 &
    p=$!
    kill $p &> /dev/null
else
    sleep 1 &
    p=$!
    kill $p &> /dev/null
    /bin/true # This line is the sole difference.
fi

命名它a.sh,然后我就得到了(在我的 Linux 机器上)

$ ./a.sh
/a.sh: line 16: 18103 Terminated              sleep 1
$ ./a.sh foo
$ # no "Terminated" message

为什么第二种情况没有消息? Bash 的基本行为是打印“Termied”(看到这个问题)。

(注意我p=$!在我的真实代码中使用,但在上面的情况下你可以使用kill $!。)

编辑:谢尔盖·科洛迪亚日内请提及问题“为何真假如此之大?”在评论中。(有点题外话,但读起来很有趣。)不幸的是,该评论所属的答案被删除了,所以我在这里记录一下。谢谢Sergiy。

答案1

按照马泰奥·意大利的回答:

相反,用killall 做同样的实验,通常会立即产生“killed”消息,表明时间/上下文切换/执行外部命令所需的任何内容都会导致足够长的延迟,以便在控制权返回到 shell 之前杀死进程。

换句话说,调用外部引起的延迟/bin/true导致了延迟,这使得shell可以打印消息。

我还使用/bin/echovs进行了测试echo

#!/bin/bash
if [[ -n "$1" ]]; then
    sleep 1 &
    p=$!
    kill $p &> /dev/null
    /bin/echo "a line"
else
    sleep 1 &
    p=$!
    kill $p &> /dev/null
fi

使用这个脚本:

$ bash ./mystery.sh 
$ bash ./mystery.sh foo
a line
./mystery.sh: line 11: 10361 Terminated              sleep 1

内置echo

$ bash ./mystery.sh 
$ bash ./mystery.sh foo
a line

换句话说,调用外部可执行文件这一事实会强制 shell 在最后一个子进程返回时执行子进程和后台作业的检查。的情况下:

if [[ -n "$1" ]]; then
    sleep 1 &
    p=$!
    kill $p &> /dev/null

从您的原始脚本中,没有调用任何额外的命令,只有最后内置的命令。


除此之外,我还进行了一些测试strace

看起来父进程退出并且不等待子进程。换句话说,shell 的父进程退出得太早执行显式检查。

$ strace -s 1024 -e kill bash mystery.sh 
kill(9830, SIGTERM)                     = 0
mystery.sh: line 11:  9830 Terminated              sleep 1
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=9830, si_uid=1000, si_status=SIGTERM, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
$ strace -s 1024 -e kill bash mystery.sh  foo
kill(9839, SIGTERM)                     = 0
+++ exited with 0 +++

值得注意的是,在使用位置参数进行跟踪时,等待调用也不存在:

$ strace -s 1024 -e kill,wait4 bash mystery.sh  foo
kill(9910, SIGTERM)                     = 0
+++ exited with 0 +++
$ strace -s 1024 -e kill,wait4 bash mystery.sh
kill(9916, SIGTERM)                     = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=9916, si_uid=1000, si_status=SIGTERM, si_utime=0, si_stime=0} ---
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], WNOHANG, NULL) = 9916
wait4(-1, 0x7ffe8e5bb110, WNOHANG, NULL) = -1 ECHILD (No child processes)
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 9917
mystery.sh: line 11:  9916 Terminated              sleep 1
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9917, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, 0x7ffe8e5bb250, WNOHANG, NULL) = -1 ECHILD (No child processes)
+++ exited with 0 +++

相关内容