Réponse courte
Dans bash
(et dash
), les différents messages "d'état du travail" ne sont pas affichés par les gestionnaires de signaux, mais nécessitent une vérification explicite. Cette vérification n'est effectuée qu'avant qu'une nouvelle invite ne soit fournie, probablement pour ne pas déranger l'utilisateur pendant qu'il / elle tape une nouvelle commande.
Le message n'est pas affiché juste avant l'invite après que kill
s'affiche probablement parce que le processus n'est pas encore mort - c'est une condition particulièrement probable car kill
c'est une commande interne du shell, donc il est très rapide à exécuter et n'a pas besoin de bifurquer.
Faire la même expérience avec killall
, au lieu de cela, donne généralement le message "tué" immédiatement, signe que l'heure / le contexte change / tout ce qui est nécessaire pour exécuter une commande externe entraîne un délai suffisamment long pour que le processus soit tué avant que le contrôle ne revienne au shell .
matteo@teokubuntu:~$ dash
$ sleep 60 &
$ ps
PID TTY TIME CMD
4540 pts/3 00:00:00 bash
4811 pts/3 00:00:00 sh
4812 pts/3 00:00:00 sleep
4813 pts/3 00:00:00 ps
$ kill -9 4812
$
[1] + Killed sleep 60
$ sleep 60 &
$ killall sleep
[1] + Terminated sleep 60
$
Longue réponse
dash
Tout d'abord, j'ai regardé les dash
sources, car dash
présente le même comportement et le code est sûrement plus simple que bash
.
Comme indiqué ci-dessus, le point semble être que les messages d'état des travaux ne sont pas émis par un gestionnaire de signaux (ce qui peut interrompre le flux de contrôle shell "normal"), mais ils sont la conséquence d'une vérification explicite (un showjobs(out2, SHOW_CHANGED)
appel entrant dash
) qui est effectuée seulement avant de demander une nouvelle entrée à l'utilisateur, dans la boucle REPL.
Ainsi, si le shell est bloqué en attendant l'entrée de l'utilisateur, aucun message de ce type n'est émis.
Maintenant, pourquoi la vérification effectuée juste après la mise à mort ne montre-t-elle pas que le processus s'est réellement terminé? Comme expliqué ci-dessus, probablement parce que c'est trop rapide. kill
est une commande interne du shell, il est donc très rapide à exécuter et n'a pas besoin de bifurquer, donc, immédiatement après kill
la vérification, le processus est toujours en vie (ou, au moins, est toujours en cours de destruction).
bash
Comme prévu, bash
étant un shell beaucoup plus complexe, il était plus délicat et nécessitait du gdb
-fu.
La trace pour quand ce message est émis est quelque chose comme
(gdb) bt
#0 pretty_print_job (job_index=job_index@entry=0, format=format@entry=0, stream=0x7ffff7bd01a0 <_IO_2_1_stderr_>) at jobs.c:1630
#1 0x000000000044030a in notify_of_job_status () at jobs.c:3561
#2 notify_of_job_status () at jobs.c:3461
#3 0x0000000000441e97 in notify_and_cleanup () at jobs.c:2664
#4 0x00000000004205e1 in shell_getc (remove_quoted_newline=1) at /Users/chet/src/bash/src/parse.y:2213
#5 shell_getc (remove_quoted_newline=1) at /Users/chet/src/bash/src/parse.y:2159
#6 0x0000000000423316 in read_token (command=<optimized out>) at /Users/chet/src/bash/src/parse.y:2908
#7 read_token (command=0) at /Users/chet/src/bash/src/parse.y:2859
#8 0x00000000004268e4 in yylex () at /Users/chet/src/bash/src/parse.y:2517
#9 yyparse () at y.tab.c:2014
#10 0x000000000041df6a in parse_command () at eval.c:228
#11 0x000000000041e036 in read_command () at eval.c:272
#12 0x000000000041e27f in reader_loop () at eval.c:137
#13 0x000000000041c6fd in main (argc=1, argv=0x7fffffffdf48, env=0x7fffffffdf58) at shell.c:749
L'appel qui vérifie les emplois morts & co. est notify_of_job_status
(c'est plus ou moins l'équivalent de showjobs(..., SHOW_CHANGED)
in dash
); # 0- # 1 sont liés à son fonctionnement interne; 6-8 est le code de l'analyseur généré par yacc; 10-12 est la boucle REPL.
L'endroit intéressant ici est le # 4, c'est-à-dire d'où notify_and_cleanup
vient l' appel. Il semble que bash
, contrairement à dash
, peut vérifier les travaux terminés à chaque caractère lu à partir de la ligne de commande, mais voici ce que j'ai trouvé:
/* If the shell is interatctive, but not currently printing a prompt
(interactive_shell && interactive == 0), we don't want to print
notifies or cleanup the jobs -- we want to defer it until we do
print the next prompt. */
if (interactive_shell == 0 || SHOULD_PROMPT())
{
#if defined (JOB_CONTROL)
/* This can cause a problem when reading a command as the result
of a trap, when the trap is called from flush_child. This call
had better not cause jobs to disappear from the job table in
that case, or we will have big trouble. */
notify_and_cleanup ();
#else /* !JOB_CONTROL */
cleanup_dead_jobs ();
#endif /* !JOB_CONTROL */
}
Ainsi, en mode interactif, il est intentionnel de retarder la vérification jusqu'à ce qu'une nouvelle invite soit fournie, probablement pour ne pas déranger l'utilisateur qui entre des commandes. Quant à savoir pourquoi la vérification ne détecte pas le processus mort lors de l'affichage de la nouvelle invite immédiatement après le kill
, l'explication précédente est valable (le processus n'est pas encore mort).
pid="$(sh -c 'cat "$fileName" |less & echo ${!}')"
mais moins n'apparaîtront pas