Come fare in modo condizionale qualcosa se un comando ha esito positivo o negativo


283

Come posso fare qualcosa del genere in bash?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi

Risposte:


359

Come fare in modo condizionale qualcosa se un comando ha esito positivo o negativo

Questo è esattamente ciò ifche fa l'affermazione di Bash :

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Aggiunta di informazioni dai commenti: in questo caso non è necessario utilizzare la sintassi [... è esso stesso un comando, quasi equivalente a . È probabilmente il comando più comune da utilizzare in un , il che può portare a supporre che faccia parte della sintassi della shell. Ma se si desidera verificare se un comando ha avuto esito positivo o meno, utilizzare il comando stesso direttamente con , come mostrato sopra.][testifif

Modifica: cita la domanda in alto per maggiore chiarezza (questa risposta non viene visualizzata nella parte superiore della pagina).


11
Si noti che il punto e virgola è importante.
Thorbjørn Ravn Andersen,

21
Oppure puoi semplicemente mettere thensu una linea separata.
l0b0

36
@Joe: penso che intendi if ! command ; then ... ; fi. [è esso stesso un comando e non è necessario in questo caso.
Keith Thompson,

20
@Joe: Il mio modo ha anche la virtù di essere corretto. if [ ! command ]non esegue command; tratta commanduna stringa e la considera vera perché ha una lunghezza diversa da zero. [è sinonimo del testcomando
Keith Thompson,

5
Ops. Hai ragione.
Joe,

133

Per le piccole cose che vuoi che accadano se un comando di shell funziona, puoi usare il &&costrutto:

rm -rf somedir && trace_output "Removed the directory"

Allo stesso modo per le piccole cose che vuoi che accadano quando un comando di shell fallisce, puoi usare ||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

O entrambi

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Probabilmente non è saggio fare molto con questi costrutti, ma a volte possono rendere il flusso di controllo molto più chiaro.


3
Sono più brevi e (almeno in alcune conchiglie) più veloci. Rabbrividisco nel ricordare un mostro script di installazione di Ultrix scritto con solo queste costruzioni condizionali che una volta ho provato a decifrare ...
vonbrand,

101

Controllare il valore di $?, che contiene il risultato dell'esecuzione del comando / funzione più recente:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

11
Mentre tecnicamente corretto (e quindi non garantisce un downvote), non sta usando il iflinguaggio di Bash . Preferisco la risposta di Keith Thompson.
Janmoesen,

16
Ci sono dei vantaggi in questo linguaggio: preserva il valore di ritorno. In tutto, trovo che questo sia più potente, anche se più dettagliato. è anche più facile da leggere.
taxilian

2
Che cos'è "Bash's if idiom"?
Nowaker,

3
@Nowaker Il fatto che l'unico scopo ifè quello di farlo. Le condizioni di controllo del flusso in Bash tutte esaminano $?dietro le quinte; è quello che fanno. L'esame esplicito del suo valore dovrebbe essere superfluo nella stragrande maggioranza dei casi ed è generalmente un antipasto per principianti.
Tripleee,

1
@lunakid Se non ti interessa il codice di uscita, if ! cmdva bene. Altrimenti, un accordo comune è usare una elseclausola. Non puoi avere un vuoto, thenma a volte vedi then : nothing; elsedove il :no-op è significativo. truefunzionerebbe anche lì.
Tripleee

47

Questo ha funzionato per me:

command && echo "OK" || echo "NOK"

se commandriesce, echo "OK"viene eseguito e, poiché ha esito positivo, l'esecuzione si interrompe lì. Altrimenti, &&viene saltato ed echo "NOK"eseguito.


5
Se vuoi fare qualcosa se fallisce e preservare il codice di uscita (da mostrare nel prompt dei comandi o test in uno script), puoi farlo: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Sam Hasler,

2
@ Sam-Hasler: non dovrebbe essere command && echo "OK" || (c=$?; echo "NOK"; (exit $c))?
jrw32982,

9
Inoltre, se la echo "OK"parte potrebbe fallire, allora è meglio:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982

@ jrw32982, Nizza, ho usato la prima costruzione, ma non la seconda.
Sam Hasler,

La vera risposta è nei commenti, grazie a tutti!
Joshua Pinter,

6

Va notato che if...then...fie &&/ ||tipo di approccio riguarda lo stato di uscita restituito dal comando che vogliamo testare (0 in caso di successo); tuttavia, alcuni comandi non restituiscono uno stato di uscita diverso da zero se il comando non è riuscito o non è in grado di gestire l'input. Ciò significa che i soliti ife &&/ ||approcci non funzioneranno per quei comandi particolari.

Ad esempio, su Linux GNU fileesce ancora con 0 se ha ricevuto un file inesistente come argomento e findnon è riuscito a individuare il file specificato dall'utente.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

In tali casi, un potenziale modo in cui potremmo gestire la situazione è la lettura stderr/ stdinmessaggi, ad esempio quelli restituiti dal filecomando o l'analisi dell'output del comando come in find. A tal fine, casepotrebbe essere utilizzata la dichiarazione.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found

2

Il più soggetto a errori che ho potuto inventare era:

  • Innanzitutto, ottieni il valore. Supponi di fare qualcosa come:

RR=$?

Ora, non solo per questa situazione, ma per altre persone che potresti incontrare, considera:

variabile definita:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Risposta: si

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Risposta: no

variabile non definita:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Risposta: no

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Risposta: si

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Risposta: si

Questo impedisce errori di ogni genere.

Probabilmente sei a conoscenza di tutta la sintassi, ma qui alcuni suggerimenti:

  • Usa le virgolette. Evita "blank"di essere nothing.
  • La nuova notazione moderna per le variabili è ${variable}.
  • L'aggiunta di uno zero concatenato prima del numero evita anche "nessun numero".
  • Ma aspetta, l'aggiunta di uno zero fa diventare il numero base-8. Verrà visualizzato un errore come:
    • value too great for base (error token is "08")per i numeri sopra 7. Questo è quando 10#entra in gioco:
    • 10#forza il numero per essere base-10.

1

Puoi farlo:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi

9
Funziona ma è contorto. Stai eseguendo il ping in una subshell di una subshell, l'output di pingviene acquisito in vista della sua esecuzione come comando. Ma poiché l'output viene reindirizzato a / dev / null che sarà sempre la stringa vuota. Quindi non si esegue nulla in una subshell, il che significa che verrà mantenuto lo stato di uscita precedente (della subshell di sostituzione comando, ovvero ping). Ovviamente, il modo corretto è if ping ...; thenqui.
Stéphane Chazelas,

1

Come notato altrove in questo thread, la domanda originale sostanzialmente risponde a se stessa. Ecco un'illustrazione che mostra che anche le ifcondizioni possono essere nidificate.

Questo esempio ifconsente di verificare se esiste un file e se si tratta di un file normale. Se tali condizioni sono vere, controlla se ha dimensioni maggiori di 0.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi

2
Questo è ciò che fa la tua risposta, ma la tua risposta non affronta la domanda.
Jeff Schaller

@JeffSchaller, grazie per la tua nota. Ho modificato il mio post per includere un riferimento alla domanda.
quarzinquartz,

1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi

3
Questo assomiglia un po 'alla risposta accettata esistente ; si prega di attribuire il lavoro che si sta riutilizzando o aggiungere il proprio impegno nelle risposte.
Jeff Schaller

-3

Questo potrebbe essere fatto semplicemente in questo modo poiché $?ti dà lo stato dell'ultimo comando eseguito.

Quindi potrebbe essere

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi

1
Downvote: questo semplicemente parafrasando una risposta precedente che ha giustamente ricevuto critiche per essere unidiomatico.
Tripleee
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.