Quando un processo viene terminato con un segnale gestibile come SIGINT
o SIGTERM
ma non gestisce il segnale, quale sarà il codice di uscita del processo?
Che dire di segnali ingestibili come SIGKILL
?
Da quello che posso dire, uccidere un processo con SIGINT
probabili risultati nel codice di uscita 130
, ma potrebbe variare in base all'implementazione del kernel o della shell?
$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130
Non sono sicuro di come testare gli altri segnali...
$ ./myScript &
$ killall myScript
$ echo $?
0 # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0 # same problem
Risposta accettata:
I processi possono chiamare _exit()
chiamata di sistema (su Linux, vedere anche exit_group()
) con un argomento intero per segnalare un codice di uscita al genitore. Sebbene sia un numero intero, solo gli 8 bit meno significativi sono disponibili per il genitore (l'eccezione è quando si utilizza waitid()
o gestore su SIGCHLD nel genitore per recuperare quel codice, anche se non su Linux).
Il genitore in genere esegue un wait()
o waitpid()
per ottenere lo stato del figlio come numero intero (sebbene waitid()
con semantica un po' diversa può essere usata pure).
Su Linux e sulla maggior parte degli Unice, se il processo terminava normalmente, i bit da 8 a 15 di quello stato number conterrà il codice di uscita passato a exit()
. In caso contrario, i 7 bit meno significativi (da 0 a 6) conterranno il numero del segnale e il bit 7 verrà impostato se è stato eseguito il dump di un core.
perl
's $?
ad esempio contiene quel numero come impostato da 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
Le shell simili a Bourne creano anche lo stato di uscita dell'ultimo comando eseguito nel proprio $?
variabile. Tuttavia, non contiene direttamente il numero restituito da waitpid()
, ma una trasformazione su di esso, ed è diverso tra le shell.
Ciò che è comune a tutte le shell è che $?
contiene gli 8 bit più bassi del codice di uscita (il numero passato a exit()
) se il processo è terminato normalmente.
Dove differisce è quando il processo viene terminato da un segnale. In tutti i casi, ed è richiesto da POSIX, il numero sarà maggiore di 128. POSIX non specifica quale può essere il valore. In pratica, però, in tutte le shell Bourne che conosco, i 7 bit più bassi di $?
conterrà il numero del segnale. Ma, dove n
è il numero del segnale,
-
in ash, zsh, pdksh, bash, la shell Bourne,
$?
è128 + n
. Ciò significa che in quelle shell, se ottieni un$?
di129
, non sai se è perché il processo è terminato conexit(129)
o se è stato ucciso dal segnale1
(HUP
sulla maggior parte dei sistemi). Ma la logica è che le shell, quando escono da sole, per impostazione predefinita restituiscono lo stato di uscita dell'ultimo comando uscito. Assicurandoti$?
non è mai maggiore di 255, che consente di avere uno stato di uscita coerente:$ 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
,$?
è256 + n
. Ciò significa che da un valore di$?
puoi distinguere tra un processo ucciso e non ucciso. Versioni più recenti diksh
, all'uscita, se$?
era maggiore di 255, si uccide con lo stesso segnale per poter segnalare lo stesso stato di uscita al genitore. Anche se sembra una buona idea, significa cheksh
genererà un core dump aggiuntivo (potenzialmente sovrascrivendo l'altro) se il processo è stato interrotto da un segnale di generazione del core:$ 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.
Dove potresti anche dire che c'è un bug è che
ksh93
si uccide anche se$?
proviene da unreturn 257
fatto da una funzione:$ 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
offre un compromesso. Restituisce256 + 128 + n
. Ciò significa che possiamo anche distinguere tra un processo terminato e uno terminato correttamente. E all'uscita riporterà128 + n
senza doversi suicidare e gli effetti collaterali che può avere.$ 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
Per ottenere il segnale dal valore di $?
, il modo portatile consiste nell'usare kill -l
:
$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM
(per la portabilità, non dovresti mai usare numeri di segnale, solo nomi di segnali)
Correlati:backup a livello di byte e backup a livello di file?Sui fronti non Bourne:
csh
/tcsh
efish
come la shell Bourne tranne per il fatto che lo stato è in$status
invece di$?
(nota chezsh
imposta anche$status
per compatibilità concsh
(oltre a$?
)).rc
:lo stato di uscita è in$status
anche, ma quando viene uccisa da un segnale, quella variabile contiene il nome del segnale (comesigterm
osigill+core
se è stato generato un core) invece di un numero, che è l'ennesima prova del buon design di quella shell.-
es
. lo stato di uscita non è una variabile. Se ti interessa, esegui il comando come:status = <={cmd}
che restituirà un numero o
sigterm
osigsegv+core
come inrc
.
Forse per completezza dovremmo citare zsh
's $pipestatus
e bash
's $PIPESTATUS
array che contengono lo stato di uscita dei componenti dell'ultima pipeline.
E anche per completezza, quando si tratta di funzioni di shell e file sorgente, per impostazione predefinita le funzioni ritornano con lo stato di uscita dell'ultimo comando eseguito, ma possono anche impostare uno stato di ritorno in modo esplicito con return
incorporato. E vediamo alcune differenze qui:
bash
emksh
(da R41, una regressione^Wchange apparentemente introdotta intenzionalmente) troncherà il numero (positivo o negativo) a 8 bit. Quindi, ad esempio,return 1234
imposterà$?
a210
,return -- -1
imposterà$?
a 255.zsh
epdksh
(e derivati diversi damksh
) consente qualsiasi intero decimale a 32 bit con segno (da -2 a 2-1) (e tronca il numero a 32 bit).ash
eyash
consentire qualsiasi numero intero positivo compreso tra 0 e 2-1 e restituire un errore per qualsiasi numero al di fuori di quello.ksh93
perreturn 0
areturn 320
impostare$?
così com'è, ma per qualsiasi altra cosa, troncare a 8 bit. Attenzione come già accennato che restituire un numero compreso tra 256 e 320 potrebbe causareksh
uccidersi all'uscita.rc
ees
consentire la restituzione di qualsiasi elenco anche.
Nota anche che alcune shell usano anche valori speciali di $?
/$status
per segnalare alcune condizioni di errore che non sono lo stato di uscita di un processo, come 127
o 126
per comando non trovato o non eseguibile (o errore di sintassi in un file sorgente)...