Cuando se elimina un proceso con una señal manejable como SIGINT
o SIGTERM
pero no maneja la señal, ¿cuál será el código de salida del proceso?
¿Qué pasa con señales inmanejables como SIGKILL
? ?
Por lo que puedo decir, matar un proceso con SIGINT
probablemente resulte en el código de salida 130
, pero ¿varía eso según la implementación del kernel o shell?
$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130
No estoy seguro de cómo probaría las otras señales...
$ ./myScript &
$ killall myScript
$ echo $?
0 # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0 # same problem
Respuesta aceptada:
Los procesos pueden llamar al _exit()
llamada al sistema (en Linux, consulte también exit_group()
) con un argumento entero para informar un código de salida a su padre. Aunque es un número entero, solo los 8 bits menos significativos están disponibles para el padre (la excepción es cuando se usa waitid()
o controlador en SIGCHLD en el padre para recuperar ese código, aunque no en Linux).
El padre normalmente hará wait()
o waitpid()
para obtener el estado de su hijo como un número entero (aunque waitid()
también se puede usar una semántica algo diferente).
En Linux y la mayoría de Unices, si el proceso terminó normalmente, los bits 8 a 15 de ese estado número contendrá el código de salida como se pasa a exit()
. De lo contrario, los 7 bits menos significativos (0 a 6) contendrán el número de señal y el bit 7 se establecerá si se descargó un núcleo.
perl
's $?
por ejemplo, contiene ese número establecido por waitpid()
:
$ perl -e 'system q(kill $$); printf "%04xn", $?'
000f # killed by signal 15
$ perl -e 'system q(kill -ILL $$); printf "%04xn", $?'
0084 # killed by signal 4 and core dumped
$ perl -e 'system q(exit $((0xabc))); printf "%04xn", $?'
bc00 # terminated normally, 0xbc the lowest 8 bits of the status
Los shells tipo Bourne también hacen que el estado de salida del último comando de ejecución sea su propio $?
variable. Sin embargo, no contiene directamente el número devuelto por waitpid()
, pero una transformación en él, y es diferente entre shells.
Lo que es común entre todos los shells es que $?
contiene los 8 bits más bajos del código de salida (el número pasado a exit()
) si el proceso terminó normalmente.
Donde difiere es cuando el proceso termina con una señal. En todos los casos, y eso es requerido por POSIX, el número será mayor a 128. POSIX no especifica cuál puede ser el valor. Sin embargo, en la práctica, en todos los shells tipo Bourne que conozco, los 7 bits más bajos de $?
contendrá el número de señal. Pero, donde n
es el número de la señal,
-
en ash, zsh, pdksh, bash, el shell Bourne,
$?
es128 + n
. Lo que eso significa es que en esos shells, si obtienes un$?
de129
, no sabes si es porque el proceso salió conexit(129)
o si fue eliminado por la señal1
(HUP
en la mayoría de los sistemas). Pero la razón es que los shells, cuando salen por sí mismos, por defecto devuelven el estado de salida del último comando que salió. Asegurándose de que$?
nunca es mayor a 255, eso permite tener un estado de salida consistente:$ bash -c 'sh -c "kill $$"; printf "%xn" "$?"' bash: line 1: 16720 Terminated sh -c "kill $$" 8f # 128 + 15 $ bash -c 'sh -c "kill $$"; exit'; printf '%xn' "$?" bash: line 1: 16726 Terminated sh -c "kill $$" 8f # here that 0x8f is from a exit(143) done by bash. Though it's # not from a killed process, that does tell us that probably # something was killed by a SIGTERM
-
ksh93
,$?
es256 + n
. Eso significa que a partir de un valor de$?
puede diferenciar entre un proceso eliminado y no eliminado. Versiones más recientes deksh
, al salir, si$?
era mayor que 255, se suicida con la misma señal para poder informar el mismo estado de salida a su padre. Si bien eso suena como una buena idea, eso significa queksh
generará un volcado de núcleo adicional (potencialmente sobrescribiendo el otro) si el proceso fue detenido por una señal de generación de núcleo:$ ksh -c 'sh -c "kill $$"; printf "%xn" "$?"' ksh: 16828: Terminated 10f # 256 + 15 $ ksh -c 'sh -c "kill -ILL $$"; exit'; printf '%xn' "$?" ksh: 16816: Illegal instruction(coredump) Illegal instruction(coredump) 104 # 256 + 15, ksh did indeed kill itself so as to report the same # exit status as sh. Older versions of `ksh93` would have returned # 4 instead.
Donde incluso podrías decir que hay un error es que
ksh93
se suicida incluso si$?
proviene de unreturn 257
hecho por una función:$ ksh -c 'f() { return "$1"; }; f 257; exit' zsh: hangup ksh -c 'f() { return "$1"; }; f 257; exit' # ksh kills itself with a SIGHUP so as to report a 257 exit status # to its parent
-
yash
.yash
ofrece un compromiso. Devuelve256 + 128 + n
. Eso significa que también podemos diferenciar entre un proceso muerto y uno que terminó correctamente. Y al salir, informará128 + n
sin tener que suicidarse y los efectos secundarios que puede tener.$ yash -c 'sh -c "kill $$"; printf "%xn" "$?"' 18f # 256 + 128 + 15 $ yash -c 'sh -c "kill $$"; exit'; printf '%xn' "$?" 8f # that's from a exit(143), yash was not killed
Para obtener la señal del valor de $?
, la forma portátil es usar kill -l
:
$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM
(para portabilidad, nunca debe usar números de señal, solo nombres de señal)
Relacionado:¿Copias de seguridad a nivel de byte versus a nivel de archivo?En los frentes no Bourne:
csh
/tcsh
yfish
igual que el shell de Bourne excepto que el estado está en$status
en lugar de$?
(tenga en cuenta quezsh
también establece$status
para compatibilidad concsh
(además de$?
)).rc
:el estado de salida está en$status
también, pero cuando es eliminada por una señal, esa variable contiene el nombre de la señal (comosigterm
osigill+core
si se generó un núcleo) en lugar de un número, lo cual es una prueba más del buen diseño de esa carcasa.-
es
. el estado de salida no es una variable. Si te importa, ejecuta el comando como:status = <={cmd}
que devolverá un número o
sigterm
osigsegv+core
como enrc
.
Tal vez para completar, deberíamos mencionar zsh
's $pipestatus
y bash
's $PIPESTATUS
matrices que contienen el estado de salida de los componentes de la última canalización.
Y también para completar, cuando se trata de funciones de shell y archivos de origen, las funciones predeterminadas regresan con el estado de salida de la última ejecución del comando, pero también pueden establecer un estado de retorno explícitamente con return
incorporado. Y vemos algunas diferencias aquí:
bash
ymksh
(desde R41, una regresión^Wcambio aparentemente introducido intencionalmente) truncará el número (positivo o negativo) a 8 bits. Entonces, por ejemplo,return 1234
establecerá$?
a210
,return -- -1
establecerá$?
a 255.zsh
ypdksh
(y derivados distintos demksh
) permite cualquier entero decimal de 32 bits con signo (-2 a 2-1) (y trunca el número a 32 bits).ash
yyash
permite cualquier número entero positivo de 0 a 2-1 y devuelve un error para cualquier número fuera de eso.ksh93
parareturn 0
parareturn 320
establecer$?
como está, pero para cualquier otra cosa, truncar a 8 bits. Tenga cuidado, como ya se mencionó, de devolver un número entre 256 y 320 podría causarksh
suicidarse al salir.rc
yes
permite devolver cualquier cosa, incluso listas.
También tenga en cuenta que algunos shells también usan valores especiales de $?
/$status
para informar algunas condiciones de error que no son el estado de salida de un proceso, como 127
o 126
para comando no encontrado o no ejecutable (o error de sintaxis en un archivo fuente)…