2013-05-03 15 views
6

Ho uno script bash che viene eseguito tre controlli oltre il mio codice sorgente, e quindi exit 0 se tutti i comandi sono riusciti, o exit 1 se qualcuno di loro non è riuscito:codici di uscita AND in bash

#!/bin/bash 

test1 ./src/ --test-1=option 
exit_1=$? 

test2 ./src/ test-2-options 
exit_2=$? 

test3 ./src/ -t 3 -o options 
exit_3=$? 

# Exit with error if any of the above failed 
[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] 
exit $? 

Questo codice funziona, ma sembra troppo lungo e prolisso. C'è un modo in cui questo può essere reso più bello? In particolare non sono felice con:

  • Dovendo eseguire il comando, e poi assegnare il codice di uscita a una variabile
  • dover utilizzare [[ ... ]], poi raccogliere il suo codice di uscita nella riga successiva a uscita con
  • Dovendo confrontare esplicitamente variabili da 0, come nel [[ $var -eq 0 ]], invece di trattarli come booleani

Idealmente, il risultato finale wou ld essere qualcosa di più leggibile simile:

exit_1=(test1 ./src/ --test-1=option) 
exit_2=(test2 ./src/ test-2-options) 
exit_3=(test3 ./src/ -t 3 -o options) 

# Exit with error if any of the above failed 
exit ($exit_1 && $exit_2 && $exit_3) 

Alcune cose che ho preso in considerazione:


Ottenere il codice di errore per una variabile in una riga:

exit_1=$(test1 ./src/ --test-1=option)$? 
exit_2=$(test2 ./src/ test-2-options)$? 
exit_3=$(test3 ./src/ -t 3 -o options)$? 

questo funziona e lo rende un po 'più breve, ma non ho mai visto nessun altro utilizzarlo prima. È una cosa sensata/sensata da fare? Ci sono problemi con questo?


Basta eseguire i test, e & & insieme:

test1 ./src/ --test-1=option && \ 
test2 ./src/ test-2-options && \ 
test3 ./src/ -t 3 -o options 
status=$? 

Questo fa non lavoro, come cortocircuiti bash. Se test1 fallisce, test2 e test3 non vengono eseguiti e voglio che vengano eseguiti tutti.


Detecing errori ed uscendo con || exit

[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] || exit 1 

Questo consente di risparmiare una riga di codici di uscita scomode e variabili, ma il bit importante exit 1 è ora a destra alla fine della riga in cui si può mancarlo. Idealmente, qualcosa di simile a questo dovrebbe funzionare:

exit [[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] 

Naturalmente, questo non non lavoro, come [[ restituisce la sua uscita, invece di eco esso.

exit $([[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] ; echo $?) 

fa lavoro, ma sembra ancora come un cludge orribile


non esplicitamente trattare con uscita codici-as-booleani

[[ $exit_1 && $exit_2 && $exit_3 ]] 

Questo non fa cosa speri che lo farebbe. Il modo più semplice di & & insieme tre codici di ritorno memorizzati nelle variabili è con il completo $var -eq 0 && .... Sicuramente c'è un modo più bello?


So bash non è un bel linguaggio di programmazione - se si può anche chiamare così - ma c'è un modo per rendere questo meno imbarazzante?

+0

L'approccio 'var = $ (pippo) $?' È interessante, ma mette le chiamate di prova in una subshell non necessaria. – kojiro

+0

Per lo meno, la tua finale 'exit $?' Non è necessaria se '[[... && ... && ...]]' è l'ultima affermazione dello script. – chepner

risposta

7

È possibile utilizzare il comando aritmetica s' bash-OR i codici di uscita insieme, e negare il risultato, per ottenere un codice di uscita 1 se uno dei codici è diverso da zero. In primo luogo, un esempio:

$ ! ((0 | 0 | 0)); echo $? 
0 
$ ! ((1 | 0 | 0)); echo $? 
1 

Ora, lo script:

#!/bin/bash 

test1 ./src/ --test-1=option; exit_1=$? 
test2 ./src/ test-2-options; exit_2=$? 
test3 ./src/ -t 3 -o options; exit_3=$? 

# Exit with error if any of the above failed. No need for a final 
# call to exit, if this is the last command in the script 
! (($exit_1 || $exit_2 || $exit_3)) 

o, in generale, è possibile accumulare i codici di uscita, come si esegue un numero arbitrario di test:

#!/bin/bash 

# Unfortunately, ||= is not an assignment operator in bash. 
# You could use |=, I suppose; you may not be able to assign 
# any meaning to any particular non-zero value, though. 
test1 ./src/ --test-1=option; ((exit_status = exit_status || $?)) 
test2 ./src/ test-2-options; ((exit_status = exit_status || $?)) 
test3 ./src/ -t 3 -o options; ((exit_status = exit_status || $?)) 
# ... 
testn ./src "${final_option_list[@]}"; ((exit_status = exit_status || $?)) 

exit $exit_status # 0 if they all succeeded, 1 if any failed 
+0

'! (($ exit_1 || $ exit_2 || $ exit_3)) 'è molto utile. Stavo cercando di evitare di mettere gli assegnamenti 'exit_1 = $?' Alla fine della riga, poiché alcune affermazioni importanti sono nascoste alla fine delle linee lunghe. Sono andato con la soluzione $ $ exit_1 = $ (...) $? ', Ma ho usato il tuo. Grazie! –

+0

In realtà, scarto quello. Non posso usare '$ exit_1 = $ (...) $?', Poiché raccoglie sia l'output del comando * che * il codice di ritorno. Userò semplicemente la tua intera risposta, penso. –

2

Alcuni miglioramenti

[ $exit_1$exit_2$exit3 = 000 ] 
# no exit needed here, script exits with code from last command 
0

È possibile assegnare il codice di uscita di un comando usando questa linea:

RES1=$(CMD > /dev/null)$? 

esempio:

RES1=$(test1 ./src/ --test-1=option > /dev/null)$? 

Così il vostro codice sarà:

exit_1=$(test1 ./src/ --test-1=option > /dev/null)$? 
exit_2=$(test2 ./src/ test-2-options > /dev/null)$? 
exit_3=$(test3 ./src/ -t 3 -o options > /dev/null)$? 

# Exit with error if any of the above failed 
exit ($exit_1 && $exit_2 && $exit_3) 
+0

Un trucco accurato che non conoscevo, ma questo crea una sottotitolazione non necessaria, quindi non posso davvero revocarlo in buona coscienza. – kojiro

+0

Mentre gli script di test attivano un server Web di prova e 'wget --mirror' per verificare i collegamenti interrotti, diffare più cartelle, l'uno contro l'altro e grep su alcuni file per rimanere' TODO's lasciato nei documenti, attivando una subshell è qualcosa con cui posso convivere! Questo è comunque buono a sapersi, se la velocità e l'efficienza sono sempre un problema. –

+0

In realtà, questo non funziona affatto! Se i comandi nella subshell producono un output (che i miei comandi fanno), l'output viene assegnato alla variabile. Ciò significa che 'out = $ (echo 'foo'; exit 1) $?' Risulta in '$ out' che è' 'foo1''. –

0

Perché hai bisogno di tre diverse variabili?

fn() { 
    local -i st 
    test1 ./src/ --test-1=option 
    st=$? 

    test2 ./src/ test-2-options 
    ((st = ($? || st))) # Use arithmetic expression to or the values at each step 

    test3 ./src/ -t 3 -o options 
    ((0 == ($? || st))) 
} 
1

Stavo cercando una risposta a questo me stesso e ho deciso un modo simile a quello di @ chepner ma non uso le espressioni aritmetiche di bash:

#!/bin/bash 

failed=0 
test1 ./src/ --test-1=option || failed=1 
test2 ./src/ test-2-options || failed=1 
test3 ./src/ -t 3 -o options || failed=1 

# Exit with error if any of the above failed 
if [ "$failed" -ne 0 ] ; then 
    exit 
fi 

Basta impostare un flag all'inizio e impostare il flag su 1 se una delle affermazioni fallisce. Il || è la chiave qui.

Problemi correlati