Respuesta corta
En bash
(y dash
) los diversos mensajes de "estado del trabajo" no se muestran desde los manejadores de señales, pero requieren una verificación explícita. Esta verificación se realiza solo antes de que se proporcione una nueva solicitud, probablemente para no molestar al usuario mientras está escribiendo un nuevo comando.
El mensaje no se muestra justo antes de la solicitud después de que kill
se muestre probablemente porque el proceso aún no está muerto; esta es una condición particularmente probable ya que kill
es un comando interno del shell, por lo que es muy rápido de ejecutar y no necesita bifurcación.
En su lugar, hacer el mismo experimento killall
generalmente produce el mensaje "asesinado" inmediatamente, firme que el tiempo / cambios de contexto / lo que sea necesario para ejecutar un comando externo causan un retraso lo suficientemente largo como para que el proceso se elimine antes de que el control regrese al 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
$
Respuesta larga
dash
En primer lugar, eché un vistazo a las dash
fuentes, ya que dash
exhibe el mismo comportamiento y el código es seguramente más simple que bash
.
Como se dijo anteriormente, el punto parece ser que los mensajes de estado del trabajo no se emiten desde un controlador de señal (que puede interrumpir el flujo de control de shell "normal"), sino que son la consecuencia de una verificación explícita (una showjobs(out2, SHOW_CHANGED)
llamada dash
) que se realiza solo antes de solicitar una nueva entrada del usuario, en el bucle REPL.
Por lo tanto, si el shell está bloqueado esperando la entrada del usuario, no se emite dicho mensaje.
Ahora, ¿por qué la verificación realizada justo después de matar no muestra que el proceso realmente haya terminado? Como se explicó anteriormente, probablemente porque es demasiado rápido. kill
es un comando interno del shell, por lo que es muy rápido de ejecutar y no necesita bifurcación, por lo tanto, inmediatamente después de que kill
se realiza la verificación, el proceso todavía está vivo (o, al menos, todavía se está matando).
bash
Como era de esperar, al bash
ser un caparazón mucho más complejo, era más complicado y requería un poco de gdb
fu.
La traza inversa para cuando se emite ese mensaje es algo como
(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
La llamada que verifica los trabajos muertos y co. es notify_of_job_status
(es más o menos el equivalente de showjobs(..., SHOW_CHANGED)
in dash
); # 0- # 1 están relacionados con su funcionamiento interno; 6-8 es el código del analizador generado por yacc; 10-12 es el bucle REPL.
El lugar interesante aquí es el # 4, es decir, de donde notify_and_cleanup
proviene la llamada. Parece que bash
, a diferencia de esto dash
, puede buscar trabajos terminados en cada carácter leído desde la línea de comando, pero esto es lo que encontré:
/* 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 */
}
Por lo tanto, en modo interactivo, es intencional retrasar la verificación hasta que se proporcione una nueva solicitud, probablemente para no molestar al usuario que ingresa los comandos. En cuanto a por qué la verificación no detecta el proceso inactivo cuando se muestra el nuevo aviso inmediatamente después del kill
, la explicación anterior es válida (el proceso aún no está inactivo).
pid="$(sh -c 'cat "$fileName" |less & echo ${!}')"
pero no aparecerán menos