uscita confermata tramite trap


9

Sto cercando di intercettare il Ctrl+Csegnale che richiede una conferma da parte dell'utente. La parte di intrappolamento funziona bene. Ma una volta che il segnale viene intrappolato, non ritorna alla normale esecuzione. Invece, chiude la sceneggiatura. Come farlo riprendere l'esecuzione quando l'utente preme no.

ecco il mio codice

hell()
{
echo "Do you want to quit? Press 1 for yes and 0 for no";
read n;
if [ $n == 1 ]; then
exit 1;
fi
}

trap "hell" SIGINT

find /

Risposte:


12

Cosa sta succedendo

Quando si preme Ctrl+ C, il SIGINTsegnale viene inviato all'intero gruppo di processi in primo piano . Qui viene inviato sia al findprocesso che al processo della shell chiamante. findreagisce 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 findcomando 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 waitbuilt-in per attendere che esca normalmente. Un segnale interromperà il waitbuilt-in, che è possibile eseguire in un ciclo fino a quando non si riceve un segnale che si desidera propagare. Quindi utilizzare killper 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 waitha 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_pidmodulo. Ma poi non è possibile distinguere "è waitstato 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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.