Semplici operatori logici in Bash


256

Ho un paio di variabili e voglio verificare la seguente condizione (scritta a parole, quindi il mio tentativo fallito di bash scripting):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

E nel mio tentativo fallito, ho pensato:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi

Risposte:


682

Quello che hai scritto in realtà funziona quasi (funzionerebbe se tutte le variabili fossero numeri), ma non è affatto un modo idiomatico.

  • (…)le parentesi indicano una subshell . Quello che c'è dentro non è un'espressione come in molte altre lingue. È un elenco di comandi (proprio come parentesi esterne). Questi comandi vengono eseguiti in un sottoprocesso separato, quindi qualsiasi reindirizzamento, assegnazione, ecc. Eseguito tra parentesi non ha alcun effetto al di fuori delle parentesi.
    • Con un segno di dollaro in testa, $(…)c'è una sostituzione di comando : c'è un comando tra parentesi e l'output del comando è usato come parte della riga di comando (dopo ulteriori espansioni a meno che la sostituzione non sia tra virgolette doppie, ma questa è un'altra storia ) .
  • { … }le parentesi graffe sono come parentesi in quanto raggruppano i comandi, ma influenzano solo l'analisi, non il raggruppamento. Il programma x=2; { x=4; }; echo $xstampa 4, mentre x=2; (x=4); echo $xstampa 2. (Anche le parentesi graffe richiedono spazi attorno a loro e un punto e virgola prima della chiusura, mentre le parentesi no. Questa è solo una stranezza di sintassi.)
    • Con un segno di dollaro principale, ${VAR}è un'espansione dei parametri , che si espande al valore di una variabile, con possibili trasformazioni extra.
  • ((…))le doppie parentesi racchiudono un'istruzione aritmetica , ovvero un calcolo su numeri interi, con una sintassi simile ad altri linguaggi di programmazione. Questa sintassi viene utilizzata principalmente per i compiti e nei condizionali.
    • La stessa sintassi viene utilizzata nelle espressioni aritmetiche $((…)), che si espandono al valore intero dell'espressione.
  • [[ … ]]le parentesi doppie racchiudono espressioni condizionali . Le espressioni condizionali si basano principalmente su operatori come -n $variableverificare se una variabile è vuota e -e $fileverificare se esiste un file. Ci sono anche operatori di uguaglianza delle stringhe: "$string1" == "$string2"(attenzione che il lato destro è un modello, ad esempio [[ $foo == a* ]]test se $fooinizia con awhile [[ $foo == "a*" ]]test se $fooè esattamente a*), e il familiare !, &&e ||operatori per negazione, congiunzione e disgiunzione, nonché parentesi per raggruppamento. Si noti che è necessario uno spazio attorno a ciascun operatore (ad esempio [[ "$x" == "$y" ]], non [[ "$x"=="$y" ]]) e uno spazio o un carattere come ;sia all'interno che all'esterno delle parentesi (ad esempio [[ -n $foo ]], non[[-n $foo]]).
  • [ … ]le parentesi singole sono una forma alternativa di espressioni condizionali con più stranezze (ma più vecchie e più portatili). Non scriverne nessuno per ora; inizia a preoccuparti di loro quando trovi degli script che li contengono.

Questo è il modo idiomatico di scrivere il tuo test in bash:

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

Se hai bisogno di portabilità su altre shell, questo sarebbe il modo (nota la quotazione aggiuntiva e le serie separate di parentesi attorno a ogni singolo test e l'uso =dell'operatore tradizionale anziché la ==variante ksh / bash / zsh ):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then

31
Ottimo post, il riepilogo delle parentesi è l'ideale.
KomodoDave,

10
È meglio usare ==per differenziare il confronto dall'assegnare una variabile (che è anche =)
Will Sheppard

1
Oh, intendevo parentesi singole (rotonde), scusate la confusione. Quelli in [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]non avviano un processo secondario sebbene il primo punto elenco esplicitamente dice: "Cosa c'è dentro [parentesi] non è un'espressione come in molte altre lingue" - ma sicuramente è qui! Questo è probabilmente ovvio per l'esperto mago bash, ma nemmeno per me, immediatamente. La confusione può sorgere perché le singole parentesi possono essere utilizzate in ifun'istruzione, ma non nelle espressioni tra parentesi doppie.
Peter - Ripristina Monica il

2
@protagonist ==non è davvero “più idiomatico” di =. Hanno lo stesso significato, ma ==è una variante di ksh disponibile anche in bash e zsh, mentre =è portatile. Non c'è davvero alcun vantaggio nell'utilizzare ==, e non funziona in modo semplice.
Gilles 'SO- smetti di essere malvagio' il

2
@WillSheppard Esistono già molte altre differenze tra confronto e assegnazione: il confronto è tra parentesi, l'assegnazione no; il confronto ha spazi attorno all'operatore, il compito no; l'assegnazione ha un nome di variabile a sinistra, il confronto raramente ha qualcosa che assomiglia a un nome di variabile a sinistra e puoi e dovresti comunque inserire virgolette. Puoi scrivere ==all'interno [[ … ]], se vuoi, ma =ha anche il vantaggio di lavorare [ … ], quindi ti consiglio di non prendere l'abitudine di usarlo ==.
Gilles 'SO- smetti di essere malvagio' il

34

molto vicino

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

dovrebbe funzionare.

abbattendolo

[[ $varA -eq 1 ]] 

è un confronto intero in cui as

$varB == 't1'

è un confronto di stringhe. altrimenti, sto solo raggruppando i confronti correttamente.

Le parentesi quadre doppie delimitano un'espressione condizionale. E trovo che sia una buona lettura sull'argomento: "(IBM) Demystify test, [, [[, ((, if-then-else"


Giusto per essere sicuri: la citazione 't1'non è necessaria, giusto? Perché a differenza delle istruzioni aritmetiche tra parentesi doppie, dove t1sarebbe una variabile, t1in un'espressione condizionale tra parentesi doppie è solo una stringa letterale. Cioè, [[ $varB == 't1' ]]è esattamente lo stesso [[ $varB == t1 ]], giusto?
Peter - Ripristina Monica il

6

Una versione molto portatile (anche per legacy bourne shell):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

Ciò ha l'ulteriore qualità di eseguire al massimo un solo sottoprocesso (che è il processo [), qualunque sia il sapore della shell.

Sostituisci =con -eqse le variabili contengono valori numerici, ad es

  • 3 -eq 03 è vero, ma
  • 3 = 03è falso. (confronto stringhe)

3

Ecco il codice per la versione breve dell'istruzione if-then-else:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

Prestare attenzione a quanto segue:

  1. ||e &&operandi all'interno se condizione (cioè tra parentesi tonde) sono operandi logici (o / e)

  2. ||e &&operandi all'esterno se condizione significa allora / else

Praticamente la dichiarazione dice:

if (a = 1 o b = 2) quindi "ok" altrimenti "nok"


La parentesi ( ... )crea una subshell. Potrebbe voler usare { ... }invece le parentesi graffe . Qualsiasi stato creato in una subshell non sarà visibile nel chiamante.
Clint Pachl,

0
if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
    echo STR is empty but should have a value.
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.