Cosa sta succedendo
Quando si preme Ctrl+ C, il SIGINT
segnale viene inviato all'intero gruppo di processi in primo piano . Qui viene inviato sia al find
processo che al processo della shell chiamante. find
reagisce uscendo immediatamente e la shell reagisce chiamando la trappola.
Se il codice nella trap ritorna (cioè non chiama exit
), l'esecuzione procede con il comando dopo quello che è stato interrotto dal segnale. Qui, dopo il find
comando arriva la fine dello script, quindi lo script esce immediatamente comunque. Ma puoi vedere la differenza tra l'immissione di 0 e 1 aggiungendo un altro comando:
find /
echo "find returned $?"
Un modo per fare quello che vuoi (ma probabilmente non dovresti fare)
Puoi fare quello che vuoi; ma questa parte della mia risposta riguarda più la scoperta della programmazione della shell che la risoluzione di un problema reale.
- A livello di progettazione, un segnale riavviabile non è quello che ti aspetteresti normalmente nel tipo di programmi relativamente semplici che normalmente sono gli script di shell. L'aspettativa è che Ctrl+ Cuccida lo script.
- Come vedrai di seguito, amplia comunque le capacità della shell un po 'lontano.
Se si vuole evitare di uccidere find
, è necessario avviarlo in fondo : find / &
. Quindi utilizzare il wait
built-in per attendere che esca normalmente. Un segnale interromperà il wait
built-in, che è possibile eseguire in un ciclo fino a quando non si riceve un segnale che si desidera propagare. Quindi utilizzare kill
per uccidere il lavoro.
hell () {
echo "Do you want to quit? Press 1 for yes and 0 for no"
read n
if [ "$n" = 1 ]; then
# Kill the job if it's running, then exit
if [ -n "$job_pid" ]; then kill $job_pid; fi
exit 1
fi
}
job_pid=
trap "hell" SIGINT
# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
echo "resuming wait"
done
job_pid=
echo last exit code: $?
Esistono limitazioni a questo approccio nella shell:
- C'è una condizione di competizione: se premi Ctrl+ Csubito dopo che il lavoro è terminato ma prima della
job_pid=
linea, il gestore del segnale proverà a uccidere $jobpid
, ma il processo non esiste più (anche come zombi, perché lo wait
ha già raccolto), e il processo L'ID potrebbe essere stato riutilizzato da un altro processo. Questo non è facilmente risolvibile nella shell (forse impostando un gestore per SIGCHLD
?).
- Se è necessario lo stato di restituzione dal lavoro, è necessario utilizzare il
wait $job_pid
modulo. Ma poi non è possibile distinguere "è wait
stato interrotto da un segnale" da "il lavoro è stato interrotto da un segnale" (né da "il lavoro è terminato da solo con uno stato di ritorno ≥128", ma questo è un fatto generale nella shell programmazione).
- Questo non si estenderà facilmente se non a più sottoprocessi. Si noti che il comportamento di trap e segnali è spesso sorprendente quando si va oltre le basi nella maggior parte delle implementazioni della shell (solo ksh lo fa bene).
Per superare questi limiti, usa un linguaggio più elaborato come Perl o Python.