Come rappresentare più condizioni in una shell se istruzione?


308

Voglio rappresentare più condizioni come questa:

if [ ( $g -eq 1 -a "$c" = "123" ) -o ( $g -eq 2 -a "$c" = "456" ) ]   
then  
    echo abc;  
else  
    echo efg;   
fi  

ma quando eseguo lo script, mostra

syntax error at line 15: `[' unexpected, 

dove la riga 15 è quella che mostra se ...

Cosa c'è di sbagliato in questa condizione? Immagino che qualcosa non vada nel ().


6
Non stai chiedendo delle condizioni della shell ma delle condizioni del test . L'intera espressione nel tuo esempio viene valutata da test( [) e non dalla shell. La shell valuta solo lo stato di uscita di [.
ceving il


Risposte:


381

Tecnica classica (escape metacaratteri):

if [ \( "$g" -eq 1 -a "$c" = "123" \) -o \( "$g" -eq 2 -a "$c" = "456" \) ]
then echo abc
else echo efg
fi

Ho racchiuso i riferimenti $gtra virgolette doppie; questa è una buona pratica, in generale. In senso stretto, le parentesi non sono necessarie perché la precedenza -ae la -orende corretta anche senza di esse.

Si noti che gli operatori -ae -ofanno parte delle specifiche POSIX per test, alias [, principalmente per la compatibilità con le versioni precedenti (dal momento che facevano parte della test7a Edizione UNIX, ad esempio), ma sono esplicitamente contrassegnati come "obsoleti" da POSIX. Bash (vedi espressioni condizionali ) sembra anticipare i significati classici e POSIX per -ae -ocon i suoi operatori alternativi che accettano argomenti.


Con un po 'di attenzione, puoi utilizzare l' [[operatore più moderno , ma tieni presente che le versioni in Bash e Korn Shell (ad esempio) non devono essere identiche.

for g in 1 2 3
do
    for c in 123 456 789
    do
        if [[ ( "$g" -eq 1 && "$c" = "123" ) || ( "$g" -eq 2 && "$c" = "456" ) ]]
        then echo "g = $g; c = $c; true"
        else echo "g = $g; c = $c; false"
        fi
    done
done

Esempio di esecuzione, utilizzando Bash 3.2.57 su Mac OS X:

g = 1; c = 123; true
g = 1; c = 456; false
g = 1; c = 789; false
g = 2; c = 123; false
g = 2; c = 456; true
g = 2; c = 789; false
g = 3; c = 123; false
g = 3; c = 456; false
g = 3; c = 789; false

Non è necessario citare le variabili [[come si fa [perché non è un comando separato allo stesso modo [.


Non è una domanda classica?

Lo avrei pensato. Tuttavia, esiste un'altra alternativa, vale a dire:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ]
then echo abc
else echo efg
fi

In effetti, se leggi le linee guida della "shell portatile" per lo autoconfstrumento o i relativi pacchetti, questa notazione - usando ' ||' e ' &&' - è ciò che raccomandano. Suppongo che potresti persino arrivare a:

if [ "$g" -eq 1 ] && [ "$c" = "123" ]
then echo abc
elif [ "$g" -eq 2 ] && [ "$c" = "456" ]
then echo abc
else echo efg
fi

Dove le azioni sono banali come l'eco, questo non è male. Quando il blocco di azioni da ripetere è composto da più righe, la ripetizione è troppo dolorosa ed è preferibile una delle versioni precedenti oppure è necessario racchiudere le azioni in una funzione richiamata nei diversi thenblocchi.


9
È bene sapere che le parentesi sfuggite funzionano; a parte: in questo caso particolare, le parentesi non sono nemmeno necessarie, perché in -arealtà ha una precedenza più alta di -o(a differenza &&e ||nella shell - tuttavia, all'interno dei bash [[ ... ]] condizionali , ha && anche una precedenza più alta di ||). Se si desidera evitare -ae -oper la massima robustezza e portabilità - come suggerisce la stessa pagina man POSIX - è possibile utilizzare anche i subshells per il raggruppamento:if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
mklement0

Buona soluzione, ma mi piace la forma senza tutte le parentesi e le parentesi:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK

Sono un po 'in ritardo per questo, ma è ancora un risultato di ricerca superiore, quindi vorrei solo notare che l'uso &&o ||sono anche preferibili in quanto si comporta più come i condizionali in altre lingue e ti consente di cortocircuitare. Ad esempio: if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. In questo modo, se check_inodesè vuoto, si evitano due chiamate a stat, mentre una condizione di test più ampia e complessa deve elaborare tutti gli argomenti prima dell'esecuzione (il che può anche portare a bug se si dimentica quel comportamento).
Haravikk,

182

In Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]

9
Sicuramente l'approccio migliore in bash. Per inciso: in questo caso particolare, non sono nemmeno necessarie le parentesi, perché all'interno [[ ... ]] condizionali && ha effettivamente priorità più alta rispetto ||- a differenza esterne tali condizionali.
mklement0

Esiste un modo per scoprire quale condizione corrisponde qui? Era quello in prima parentesi o l'altro?
Firelord,

1
@Firelord: dovresti separare le condizioni in un'istruzione if/ elsee avere il codice tra thene fiinserire una funzione per evitare di ripeterlo. In casi molto semplici potresti usare caseun'istruzione con ;&fallthrough (in Bash 4).
In pausa fino a ulteriore avviso.

1
Qual è lo scopo delle due parentesi quadre?
peterchaula,


37

Utilizzando /bin/bashil seguente funzionerà:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi

8

Fai attenzione se hai spazi nelle variabili stringa e controlli l'esistenza. Assicurati di citarli correttamente.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then

1
quando usiamo le parentesi graffe dopo il simbolo del dollaro !!
YouAreAwesome,

6
$ g=3
$ c=133
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
efg
$ g=1
$ c=123
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
abc

2
Questa è una subshell inutile. { ;}potrebbe essere usato invece.
phk,

2

In bash per il confronto delle stringhe, puoi usare la seguente tecnica.

if [ $var OP "val" ]; then
    echo "statements"
fi

Esempio:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
fi

0
#!/bin/bash

current_usage=$( df -h | grep 'gfsvg-gfslv' | awk {'print $5'} )
echo $current_usage
critical_usage=6%
warning_usage=3%

if [[ ${current_usage%?} -lt ${warning_usage%?} ]]; then
echo OK current usage is $current_usage
elif [[ ${current_usage%?} -ge ${warning_usage%?} ]] && [[ ${current_usage%?} -lt ${critical_usage%?} ]]; then
echo Warning $current_usage
else
echo Critical $current_usage
fi

3
Benvenuto in Stack Overflow! Questa domanda è alla ricerca di una spiegazione , non semplicemente di un codice funzionante. La tua risposta non fornisce informazioni dettagliate per l'interrogante e può essere eliminata. Si prega di modificare per spiegare ciò che provoca i sintomi osservati.
Toby Speight,

0

puoi concatenare anche più di 2 condizioni:

if [ \( "$1" = '--usage' \) -o \( "$1" = '' \) -o \( "$1" = '--help' \) ]
then
   printf "\033[2J";printf "\033[0;0H"
   cat << EOF_PRINT_USAGE

   $0 - Purpose: upsert qto http json data to postgres db

   USAGE EXAMPLE:

   $0 -a foo -a bar



EOF_PRINT_USAGE
   exit 1
fi
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.