C'è un modo per avere una funzione nel mio script bash eseguita automaticamente su qualsiasi errore di comando?


12

Sto scrivendo uno script di shell che deve eseguire un sacco di comandi e ogni comando dipende da ogni comando precedente. Se un comando fallisce, l'intero script dovrebbe fallire e chiamo una funzione di uscita. Potrei controllare il codice di uscita di ciascun comando ma mi chiedo se esiste una modalità che posso abilitare o un modo per ottenere bash per farlo automaticamente.

Ad esempio, prendi questi comandi:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


C'è un modo in cui posso in qualche modo segnalare alla shell prima di eseguire questi comandi che dovrebbe chiamare myfunc se qualcuno di loro fallisce, così che invece posso scrivere qualcosa di più pulito come:

cd foo
rm a
cd bar
rm b

Risposte:


13

È possibile utilizzare ERR trap bash per chiudere lo script se un comando restituisce uno stato maggiore di zero ed eseguire la funzione all'uscita.

Qualcosa di simile a:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

Si noti che la trap bash è ERR implicita set -o errexito set -emeno POSIX.

E la ERRtrap non viene eseguita se il comando non riuscito fa parte dell'elenco dei comandi immediatamente successivo untilo whileparola chiave, parte del test che segue le parole riservate ifo elif, parte di un comando eseguito in &&o ||elenco o se lo stato di ritorno del comando viene invertito utilizzando !.


1

Una variazione (forse) più semplice sulla risposta accettata:

  1. Utilizzare set -e per causare il fallimento di un comando per interrompere l'esecuzione di un elenco.
  2. Elenca semplicemente i tuoi comandi.
  3. Utilizzare un'istruzione if- then- elseper eseguire i comandi di gestione degli errori. Quest'ultimo pezzo è un po 'complicato. Orologio:
set -e
Se
    cmd 1                         # ad es. cd foo
     cmd 2                         # ad es. rm a
     cmd 3                         # ad es. cd bar
     cmd 4                         # ad es. rm b
poi
    impostare + e
    comandi da eseguire in caso di successo (se presente)
altro
    impostare + e
    myfunc
    altri comandi da eseguire in caso di fallimento (se presente) 
fi

La parte difficile è che si mette i comandi nella la ifparte del if- then- else, non il thenpezzo o la elseparte. Ricordiamo che la sintassi ifdell'istruzione è

se  elenco ; quindi  elencare ; [  elenco elif ; quindi  elencare ; ] ... [altro  elenco ; ] fi 
   ↑↑↑↑
La set -eracconta il guscio che, se ( ) non riesce, non dovrebbe andare avanti ed eseguire ( ), e così via lungo la linea. Se ciò accadesse a un comando al livello più esterno di uno script di shell, la shell verrebbe chiusa. Tuttavia, poiché · · · è un elenco (composto) che segue un , il fallimento di uno di questi quattro comandi fa semplicemente fallire l'intero elenco, il che provoca l' esecuzione della clausola. Se tutti e quattro i comandi hanno esito positivo, la clausola viene eseguita.cmd1cd foocmd2rm acmd1cmd2cmd3cmd4ifelsethen

In entrambi i casi, la prima cosa da fare è probabilmente disabilitare (disattivare) l' eopzione facendo set +e. Altrimenti, la sceneggiatura potrebbe essere espulsa dall'acqua se un comando myfuncnon riesce.

set -eè specificato e descritto nelle specifiche POSIX .


0

Preso la tua parola " ogni comando dipende da ogni comando precedente. Se un comando fallisce l'intero script dovrebbe fallire " letteralmente, penso che non hai bisogno di alcuna funzione speciale per trattare gli errori.

Tutto ciò che serve è incatenare i tuoi comandi con &&operatore e ||operatore, che fa esattamente quello che hai scritto.

Ad esempio questa catena si spezzerà e stamperà "qualcosa è andato storto" se uno dei comandi precedenti si è rotto (bash legge da sinistra a destra)

cd foo && rm a && cd bar && rm b || echo "something went wrong"

Esempio reale (ho creato dir foo, file a, dir bar e file b solo per una vera demo):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

E infine se tutti i comandi sono stati eseguiti correttamente (codice di uscita 0), lo script continua:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

Ciò che è importante ricordare è che con l'uso di && il comando successivo viene eseguito se il comando precedente è uscito con il codice 0 che per bash significa successo.

Se un comando va storto nella catena, allora il comando / script / qualunque cosa segue || sarà eseguito.

E solo per la cronaca, se devi eseguire diverse azioni a seconda del comando che si è rotto, puoi anche farlo con uno script classico monitorando il valore di $?cui riporta il codice di uscita del comando esattamente precedente (restituisce zero se il comando è stato eseguito correttamente o altro numero positivo se il comando non è riuscito)

Esempio:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

Produzione:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

Suggerimento: è possibile eliminare il messaggio "bash: cd: bbar: nessun file di questo tipo ..." applicando eval $comm 2>/dev/null

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.