Comment déclencher une erreur à l'aide de la commande Trap

13

J'utilise Ubuntu 12.04.2. J'essaie d'utiliser la commande "trap" pour capturer une anomalie ou une erreur dans mon script shell, mais j'essaie également de déclencher manuellement la sortie "Erreur".

J'ai essayé la sortie 1, mais cela ne déclenchera pas le signal "Erreur".

#!/bin/bash

func()
{
    exit 1
}

trap "echo hi" INT TERM ERR
func

Vous ne savez pas comment déclencher manuellement le signal de sortie "Erreur"?

forestclown
la source

Réponses:

20

le ERRpiège n'est pas d'exécuter du code lorsque le shell lui-même se termine avec un code d'erreur non nul, mais lorsqu'une commande exécutée par ce shell qui ne fait pas partie d'une condition (comme dans if cmd..., ou cmd || ......) se termine avec un non nul état de sortie (les mêmes conditions que celles qui provoquent la set -esortie du shell).

Si vous souhaitez exécuter du code à la sortie du shell avec un état de sortie différent de zéro, vous devez ajouter un piège à la EXITplace et $?y vérifier :

trap '[ "$?" -eq 0 ] || echo hi' EXIT

Notez cependant que lors d'un signal piégé, le piège de signal et le piège EXIT seraient exécutés, vous pouvez donc le faire comme suit:

unset killed_by
trap 'killed_by=INT;exit' INT
trap 'killed_by=TERM;exit' TERM
trap '
  ret=$?
  if [ -n "$killed_by" ]; then
    echo >&2 "Ouch! Killed by $killed_by"
    exit 1
  elif [ "$ret" -ne 0 ]; then
    echo >&2 "Died with error code $ret"
  fi' EXIT

Ou pour utiliser l'état de sortie comme $((signum + 128))sur les signaux:

for sig in INT TERM HUP; do
  trap "exit $((128 + $(kill -l "$sig")))" "$sig"
done
trap '
  ret=$?
  [ "$ret" -eq 0 ] || echo >&2 "Bye: $ret"' EXIT

Notez cependant que quitter normalement sur SIGINT ou SIGQUIT a des effets secondaires potentiellement gênants lorsque votre processus parent est un shell comme bashcelui qui implémente l' attente et la gestion coopérative des sorties d'interruption de terminal. Donc, vous voudrez peut-être vous assurer de vous tuer avec le même signal à la place afin de signaler à votre parent que vous avez bien été interrompu et qu'il devrait également envisager de se quitter s'il a reçu un SIGINT / SIGQUIT.

unset killed_by
for sig in INT QUIT TERM HUP; do
  trap "exit $((128 + $(kill -l "$sig"))); killed_by=$sig" "$sig"
done
trap '
  ret=$?
  [ "$ret" -eq 0 ] || echo >&2 "Bye: $ret"
  if [ -n "$killed_by" ]; then
    trap - "$killed_by" # reset handler
    # ulimit -c 0 # possibly disable core dumps
    kill -s "$killed_by" "$$"
  else
    exec "$ret"
  fi' EXIT

Si vous voulez que le ERRpiège se déclenche, exécutez simplement une commande avec un état de sortie différent de zéro comme falseou test.

Stéphane Chazelas
la source
6

Utiliser le retour, pas de sortie, pour définir l'état à la sortie d'une fonction (si la fonction tombe-through sans retour, le statut est celui de la dernière instruction exécutée.) Si vous substituez returnpour exitdans l'exemple de la question, il fonctionnera comme Je pense que vous vouliez: le piège sera déclenché sur le pseudo-signal ERR et «salut» sera imprimé. Pour des considérations supplémentaires, essayez ceci:

#!/bin/bash

func()
{
    echo 'in func'
    return 99
    echo 'still in func'
}

trap 'echo "done"' EXIT
trap 'status=$?; echo "error status is $status"; trap - EXIT; exit $status' ERR
func
echo 'returned from func'

Vous pouvez essayer diverses modifications, telles que renvoyer 0, commenter le piège ERR, ne pas annuler le piège EXIT dans le gestionnaire ERR, ne pas quitter le gestionnaire ERR, ou supprimer le retour et le placer falsecomme dernière instruction dans func.

sdenham
la source