sortie confirmée à l'aide d'un piège

9

J'essaie de piéger le Ctrl+Csignal demandant une confirmation de l'utilisateur. La partie de piégeage fonctionne bien. Mais une fois que le signal est piégé, il ne revient pas à l'exécution normale. Au lieu de cela, il quitte le script. Comment faire reprendre l'exécution lorsque l'utilisateur appuie sur non.

voici mon code

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 /
CHID
la source

Réponses:

12

Que ce passe-t-il

Lorsque vous appuyez sur Ctrl+ C, le SIGINTsignal est envoyé à l'ensemble du groupe de processus de premier plan . Ici, il est envoyé à la fois au findprocessus et au processus shell appelant. findréagit en sortant immédiatement, et l'obus réagit en appelant le piège.

Si le code dans le piège revient (c'est-à-dire qu'il n'appelle pas exit), l'exécution se poursuit avec la commande après celle qui a été interrompue par le signal. Ici, après la findcommande vient la fin du script, donc le script se ferme immédiatement de toute façon. Mais vous pouvez voir la différence entre entrer 0 et 1 en ajoutant une autre commande:

find /
echo "find returned $?"

Une façon de faire ce que vous voulez (mais que vous ne devriez probablement pas faire)

Tu peux faire ce que tu veux; mais cette partie de ma réponse concerne davantage la découverte de la programmation shell que la résolution d'un problème réel.

  • En termes de conception, un signal redémarrable n'est pas ce à quoi vous vous attendez normalement dans le genre de programmes relativement simples que les scripts shell sont normalement. On s'attend à ce que Ctrl+ Ctue le script.
  • Comme vous le verrez ci-dessous, il étend de toute façon les capacités du shell.

Si vous voulez éviter de tuer find, vous devez commencer dans l' arrière - plan : find / &. Utilisez ensuite la fonction waitintégrée pour attendre qu'elle se termine normalement. Un signal interrompra la fonction waitintégrée, que vous pourrez exécuter en boucle jusqu'à ce que vous receviez un signal que vous souhaitez propager. Ensuite, utilisez killpour tuer le travail.

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: $?

Il y a des limites à cette approche dans le shell:

  • Il y a une condition de concurrence: si vous appuyez sur Ctrl+ Cjuste après la fin du travail mais avant la job_pid=ligne, le gestionnaire de signal essaiera de tuer $jobpid, mais le processus n'existe plus (même en tant que zombie, car il l' waita déjà récolté), et le processus L'ID peut avoir été réutilisé par un autre processus. Ce n'est pas facilement réparable dans le shell (peut-être en définissant un gestionnaire pour SIGCHLD?).
  • Si vous avez besoin du statut de retour de l'emploi, vous devez utiliser le wait $job_pidformulaire. Mais alors vous ne pouvez pas distinguer «a waitété interrompu par un signal» de «le travail a été tué par un signal» (ni de «le travail a été interrompu de lui-même avec un statut de retour ≥128», mais c'est un fait général en gros programmation).
  • Cela ne s'étendra pas facilement, voire pas du tout, à plusieurs sous-jobs. Notez que le comportement des pièges et des signaux est souvent surprenant lorsque vous allez au-delà des bases de la plupart des implémentations de shell (seul ksh le fait bien).

Pour surmonter ces limitations, utilisez un langage plus sophistiqué comme Perl ou Python.

Gilles 'SO- arrête d'être méchant'
la source