As per Matteo Italia's answer:
Doing the same experiment with killall, instead, usually yields the "killed" message immediately, sign that the time/context switches/whatever required to execute an external command cause a delay long enough for the process to be killed before the control returns to the shell.
In other words, the delay caused by calling the external /bin/true
cause a delay, which allows shell to print the message.
I've also performed tests with /bin/echo
vs 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
With this script:
$ bash ./mystery.sh
$ bash ./mystery.sh foo
a line
./mystery.sh: line 11: 10361 Terminated sleep 1
With built-in echo
:
$ bash ./mystery.sh
$ bash ./mystery.sh foo
a line
In other words, the fact that there is an external executable being called, forces the shell to perform check for child processes and background jobs when the last child returns. In case of:
if [[ -n "$1" ]]; then
sleep 1 &
p=$!
kill $p &> /dev/null
from your original script there's no extra command being called, only last built in.
Among other things, I've performed a few tests with strace
:
It seems that the parent process exits and doesn't wait for the child. In other words, parent process of the shell exits too early to perform an explicit check.
$ 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 +++
Notably, in tracing with positional parameter, the wait call is also absent:
$ 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 +++
/bin/true
with justtrue
(tested withkill $!
rather than using a variable). – l0b0 May 27 '18 at 06:27<pid> Terminated
won't be shown. There's no necessity for such an implementation; it's the designers' choice. (Of course Sergiy got a hint from the mentioned answer to solve my question, which I myself failed to.) – teika kazura Jun 06 '18 at 12:57