web-dev-qa-db-fra.com

Quitter le script shell d'un sous-(-shell

Considérons cet extrait:

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

Normalement, lorsque func est appelé, cela entraînera la terminaison du script, qui est le comportement prévu. Cependant, si elle est exécutée dans une sous-coquille, telle que dans

result=`func`

il ne quittera pas le script. Cela signifie que le code d'appel doit vérifier l'état de sortie de la fonction à chaque fois. Y a-t-il un moyen d'éviter cela? Est-ce quoi set -e est pour?

32
Ernest A C

Vous pourrait tuer la coque d'origine (kill $$) Avant d'appeler exit, et cela fonctionnerait probablement. Mais:

  • cela me semble plutôt moche
  • il va casser si vous avez un deuxième sous-vase là-bas, c'est-à-dire que vous utilisez un sous-vase dans un sous-vase.

Au lieu de cela, vous pouvez utiliser l'un des plusieurs façons de transmettre une valeur dans la FAQ Bash . La plupart d'entre eux ne sont pas si géniaux, malheureusement. Vous pouvez simplement être bloqué vérifier des erreurs après chaque appel de fonction ( -e a beaucoup de problèmes ). Soit cela, soit passer à Perl.

10
derobert

Vous pouvez décider que le statut de sortie 77 par exemple signifie quitter n'importe quel niveau de sous-vase et faire

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either

set -E En combinaison avec ERR Traps est un peu comme une version améliorée de set -e En ce qu'elle vous permet de définir votre propre manipulation d'erreurs.

Dans ZSH, les pièges ERR sont hérités automatiquement, de sorte que vous n'avez pas besoin de set -E, Vous pouvez également définir des pièges sous forme TRAPERR() fonctions et les modifier via $functions[TRAPERR], Comme functions[TRAPERR]="echo was here; $functions[TRAPERR]"

38
Stéphane Chazelas

Comme une alternative à kill $$, vous pouvez aussi essayer kill 0, cela fonctionnera dans le cas des sous-espaces imbriqués (tous les appelants et processus secondaires recevront le signal) ... mais c'est toujours brutal et moche.

7
Stéphane Gimenez

(Bash Réponse spécifique) Bash n'a aucun concept d'exception. Toutefois, avec Set -o Errexit (ou l'équivalent: Set -e) au niveau ultime, la commande défaillante entraînera le sous-groupe sortant d'un statut de sortie non nul. S'il s'agit d'un ensemble de sous-vases imbriquées sans conditionnels autour de l'exécution de ces sous-vases, il "roulera efficacement" tout le script et la sortie.

Cela peut être difficile lorsque vous essayez d'inclure des bits de divers codes Bash dans un script plus grand. Un morceau de bash peut fonctionner juste bien seul, mais lorsqu'il est exécuté sous Errexit (ou sans errexit), se comporter de manière inattendue.

[.____] [192.168.13.16 (F0F5E19E) ~ 22:58:22] # Bash -o Errexit /tmp/foo
Something a mal tourné [.____ : 58: 31] # Bash /tmp/foo
Something a mal tourné 
 Mais nous sommes arrivés ici quand même 
 [192.168.13.16 (f0f5E19e) ~ 22:58:37] # /tmp/foo
#!/bin/bash
Stop () {
 echo "$ {1}" 
} 
 [.____] si faux; alors [.____] echo "foo" 
 [.____] [.____] ([.____] (
 STOP "Quelque chose s'est mal passé" [ de toute façon "
 fi [.____] [192.168.13.16 (f0f5e19e) ~ 22:58:40] # 
0
Brian Chrisman

Essaye ça ...

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if $1; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

echo "Shell..."
func $1

echo "subshell..."
result=`func $1`

echo "Shell..."
echo "result=$result"

Les résultats que je reçois sont ...

# test_exitsubshell true
Shell...
foo
subshell...
Shell...
result=foo
# test_exitsubshell false
Shell...
something went wrong

Remarques

  • Paramétré pour autoriser le test if _ tester true ou false (voir les 2 exécutions)
  • Lorsque le test if est false, nous n'atteignons jamais le sous-groupe.
0
DocSalvager