Comment quitter un script shell si une partie de celui-ci échoue?

51

Comment puis-je écrire un script shell qui se termine, si une partie de celui-ci échoue? Par exemple, si l'extrait de code suivant échoue, le script doit quitter.

n=0
until [ $n -ge 5 ]
do
  gksu *command* && break
  n=$[$n+1]
  sleep 3
Weylyn
la source

Réponses:

88

Une approche serait d'ajouter set -eau début de votre script. Cela signifie (de help set):

  -e  Exit immediately if a command exits with a non-zero status.

Donc, si l'une de vos commandes échoue, le script se ferme.

Vous pouvez également ajouter des exitinstructions explicites aux points de défaillance possibles:

command || exit 1
terdon
la source
5
Moi (et le wiki bash ) préférerions que les gens réfléchissent au traitement correct des erreurs plutôt que d'utiliser la fonctionnalité (interrompue par conception OMI) set -e. Cela ne s'applique pas vraiment ici cependant. L'OP veut quitter le script après 5 tentatives infructueuses d'exécution de la commande.
Stéphane Chazelas
1
@ StéphaneChazelas Je ne discuterai pas avec vous de savoir si c'est cassé, je suis sûr que vous avez raison. Cependant, l'OP a demandé: "Comment puis-je écrire un script shell qui existe, si une partie de celui-ci échoue?", Qu'est-ce qui vous fait penser qu'il s'agit de quitter après 5 échecs?
terdon
parce que je ne vois pas d'autre moyen d'interpréter la question.
Stéphane Chazelas
1
@ StéphaneChazelas, vous avez peut-être raison. Je l'ai interprété littéralement: comment le script tout entier peut-il sortir si une partie de celui-ci échoue. Et set -ec’est le seul moyen que je connaisse pour le faire.
terdon
Dans cette partie de script, les commandes pouvant être déclenchées set -esont sleep( breakétant une construction spéciale, le script se ferme en cas d'échec dans la plupart des shells, les commandes situées ifà gauche ou à gauche de &&ne sont pas affectées par set -e, n=...peuvent échouer si elles nsont en lecture seule, puis cela sortirait du script sans set -eaussi bien), de sorte que l'interprétation semble assez improbable. Je conviens que la question est mal formulée.
Stéphane Chazelas
17

Vous pouvez quitter un script n'importe où en utilisant le mot clé exit. Vous pouvez également spécifier un code de sortie afin d'indiquer à d'autres programmes ce qui a été fait ou comment votre script a échoué, par exemple exit 1ou exit 2etc. les codes supérieurs à 127 sont réservés pour une terminaison anormale (par exemple par un signal)).

La construction générique pour sortir en cas d'échec est

if [ failure condition ]; then
    exit n
fi

avec approprié failure conditionet n. Mais dans des scénarios spécifiques, vous pouvez procéder différemment. En ce qui concerne votre cas, j’interprète votre question, à savoir que si l’une des cinq invocations d’ gksuéchec échouait, vous vouliez vous retirer. Une façon est d'utiliser une fonction comme celle-ci

function try_command {
    for i in 1 2 3 4 5 ; do
        if gksu command ; then
            return 0
        fi
    fi
    exit 1
}

et ensuite, invoquer la boucle par try_command.

Il existe des moyens (plus) avancés ou sophistiqués pour répondre à votre question. Cependant, la solution ci-dessus est plus accessible aux débutants que, par exemple, la solution de Stéphane.

contre mode
la source
10
attempt=0
until gksu command; do
  attempt=$((attempt + 1))
  if [ "$attempt" -gt 5 ]; then
    exit 1
  fi
done

exitquitte le script sauf s'il est appelé dans un sous-shell. Si cette partie du script est dans un sous - shell, par exemple parce qu'il est à l' intérieur (...)ou $(...)ou partie d'un pipe-line, il ne quittera que sous - shell .

Dans ce cas, si vous souhaitez que le script se ferme en plus du sous-shell, vous devez faire appel exità ce sous-shell.

Par exemple, ici avec 2 niveaux imbriqués de sous-shell:

(
  life=hard
  output=$(
    echo blah
    [ "$life" = easy ] || exit 1 # exit subshell
    echo blih not run
  ) || exit # if the subshell exits with a non-zero exit status,
            # exit as well with the same exit status

  echo not run either
) || exit # if the subshell exits with a non-zero exit status,
          # exit as well with the same exit status

Cela peut devenir plus compliqué si le sous-shell fait partie d'un pipeline. basha un spécial $PIPESTATUStableau, semblable à zshl » $pipestatusun qui peut vous aider ici:

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi
Stéphane Chazelas
la source
3

Le piège effectuera une action lors de la réception d'un signal.

trap "echo EXIT;  exit" 0
trap "echo HUP;   exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT;  exit" 3
trap "echo ERR;   exit" ERR
n=0
until [ $n -ge 5 ]
do
  n=$[$n+1]
  echo $n
  sleep 3
done

Exécutez ceci et laissez-le sortir normalement. Il intercepte le signal 0.

EXIT

Lancez-le à nouveau et interrompez avec ^ C. Il intercepte le signal 2 et le signal 0.

CTL-C
EXIT

Un état de sortie non nul s'interrompt en ERR

ERR
EXIT
RobP
la source