Come uscire da uno script di shell se una parte di esso fallisce?


51

Come posso scrivere uno script shell che esce, se una parte di esso fallisce? Ad esempio, se il seguente frammento di codice fallisce, lo script dovrebbe uscire.

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

Risposte:


88

Un approccio sarebbe quello di aggiungere set -eall'inizio della tua sceneggiatura. Ciò significa (da help set):

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

Quindi, se uno qualsiasi dei tuoi comandi fallisce, lo script verrà chiuso.

In alternativa, è possibile aggiungere exitistruzioni esplicite nei possibili punti di errore:

command || exit 1

5
Io (e il wiki di bash ) preferirei che le persone pensassero alla corretta gestione degli errori piuttosto che usare la funzionalità (rotta secondo il design IMO) set -e. In realtà non si applica qui. L'OP vuole uscire dallo script dopo 5 tentativi falliti di eseguire il comando.
Stéphane Chazelas,

1
@ StéphaneChazelas Non discuterò con te sul fatto che sia rotto, sono sicuro che tu abbia ragione. Tuttavia, l'OP ha chiesto "Come posso scrivere uno script di shell che esce, se una parte fallisce?", Cosa ti fa pensare che si tratti di uscire dopo 5 errori?
Terdon

perché non riesco a pensare ad altro modo in cui la domanda possa essere interpretata.
Stéphane Chazelas,

1
@ StéphaneChazelas potresti avere ragione. L'ho interpretato alla lettera: come può uscire l'intero script se una parte di esso fallisce. Ed set -eè l'unico modo che conosco per farlo.
Terdon

In quello script snipet, i comandi che potrebbero innescare set -esono sleep( breakessere un builtin speciale causerebbe la chiusura dello script in caso di fallimento nella maggior parte delle shell, i comandi dentro ifo alla sinistra di &&non sono interessati set -e, n=...potrebbero fallire se nè di sola lettura, ma poi anche questo uscirebbe dalla sceneggiatura set -e), quindi l'interpretazione sembra abbastanza improbabile. Sono d'accordo che la domanda sia formulata male.
Stéphane Chazelas,

17

Puoi uscire da uno script in qualsiasi luogo usando la parola chiave exit. Puoi anche specificare un codice di uscita per indicare ad altri programmi che o come lo script è fallito, ad esempio exit 1o exit 2ecc. (Per convenzione, il codice di uscita 0 ha esito positivo e qualsiasi cosa maggiore di 0 indica un errore; tuttavia, anche per convenzione, esci i codici superiori a 127 sono riservati per terminazioni anomale (ad es. mediante un segnale)).

La costruzione generica per uscire in caso di fallimento è

if [ failure condition ]; then
    exit n
fi

con adatto failure conditione n. Ma in scenari specifici puoi procedere diversamente. Ora per il tuo caso interpreto la tua domanda che se una delle cinque invocazioni gksufallisce, allora intendi uscire. Un modo è usare una funzione come questa

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

e quindi, invoca il loop di try_command.

Esistono (più) modi avanzati o sofisticati per affrontare la tua domanda. Tuttavia, la soluzione sopra è più accessibile ai principianti rispetto, per esempio, alla soluzione di Stephane.


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

exitesce dallo script, a meno che non venga chiamato in una subshell. Se quella parte dello script si trova in una subshell, ad esempio perché è all'interno (...)o $(...)o parte di una pipe-line, allora uscirà solo da quella subshell .

In tal caso, se si desidera che lo script esca in aggiunta alla subshell, è necessario richiamare exitl'uscita della subshell.

Ad esempio, qui con 2 livelli nidificati di subshells:

(
  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

Può diventare più complicato se la subshell fa parte di una pipeline. bashha una speciale $PIPESTATUSmatrice, simile a zsh's $pipestatusuno che può aiutare qui:

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi

3

La trap eseguirà un'azione alla ricezione di un segnale.

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

Esegui questo e lascialo uscire normalmente. Intrappola sul segnale 0.

EXIT

Eseguire di nuovo e interrompere con ^ C. Intrappola sia il segnale 2 che il segnale 0.

CTL-C
EXIT

Uno stato di uscita diverso da zero eseguirà il trap su ERR

ERR
EXIT
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.