Por que o bash 'nem sempre' mostra a mensagem 'Terminado' depois de matar um processo? [duplicado]

4

Por que essa diferença é importante? Os dois blocos no código abaixo diferem pela última linha:

#!/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

Nomeie-o a.sh , e eu recebo (na minha caixa Linux)

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

Por que nenhuma mensagem no segundo caso? O comportamento básico do Bash é imprimir "Terminado" ( veja esta queosção ).

(NB eu uso p=$! no meu código verdadeiro, mas no caso acima você pode usar kill $! .)

EDIT: Sergiy Kolodyazhnyy gentilmente se refere à pergunta " Por que true e false são tão grandes? " em um comentário. (É mais uma digressão, mas uma leitura divertida.) Infelizmente, a resposta a qual o comentário pertenceu foi excluída, então eu a gravo aqui. Obrigado Sergiy.

    
por teika kazura 27.05.2018 / 08:15

1 resposta

3

De acordo com a resposta de Matteo Italia :

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.

Em outras palavras, o atraso causado pela chamada do /bin/true externo causa um atraso, o que permite ao shell imprimir a mensagem.

Eu também realizei testes com /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

Com este script:

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

com echo :

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

Em outras palavras, o fato de haver um executável externo sendo chamado, força o shell a executar a verificação de processos filhos e trabalhos em segundo plano quando o último filho retornar. Em caso de:

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

do seu script original, não há comando extra sendo chamado, apenas o último construído.

Entre outras coisas, realizei alguns testes com strace :

Parece que o processo pai sai e não espera pela criança. Em outras palavras, o processo pai do shell sai muito cedo para executar uma verificação explícita.

$ 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 +++

Notadamente, no rastreamento com parâmetro posicional, a chamada de espera também está ausente:

$ 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 +++
    
por 27.05.2018 / 08:47