Gli operatori &&
e non||
sono sostituzioni in linea esatte per if-then-else. Anche se usati con attenzione, possono fare più o meno la stessa cosa.
Un singolo test è semplice e inequivocabile ...
[[ A == A ]] && echo TRUE # TRUE
[[ A == B ]] && echo TRUE #
[[ A == A ]] || echo FALSE #
[[ A == B ]] || echo FALSE # FALSE
Tuttavia, il tentativo di aggiungere più test può produrre risultati imprevisti ...
[[ A == A ]] && echo TRUE || echo FALSE # TRUE (as expected)
[[ A == B ]] && echo TRUE || echo FALSE # FALSE (as expected)
[[ A == A ]] || echo FALSE && echo TRUE # TRUE (as expected)
[[ A == B ]] || echo FALSE && echo TRUE # FALSE TRUE (huh?)
Perché vengono ripetuti sia FALSE che TRUE?
Quello che sta succedendo qui è che non ce ne siamo resi conto &&
e ||
sono operatori sovraccarichi che agiscono in modo diverso all'interno delle parentesi del test condizionale [[ ]]
rispetto a quelli presenti nell'elenco AND e OR (esecuzione condizionale) che abbiamo qui.
Dalla manpage di bash (modificata) ...
elenchi
Un elenco è una sequenza di una o più condotte separate da uno degli operatori;, &, &&, o ││, e facoltativamente terminate da uno di;, &, o. Di questi operatori di elenco, && e ││ hanno la stessa precedenza, seguito da; e &, che hanno la stessa precedenza.
Una sequenza di una o più nuove righe può apparire in un elenco anziché in un punto e virgola per delimitare i comandi.
Se un comando viene terminato dall'operatore di controllo &, la shell esegue il comando in background in una subshell. La shell non attende il completamento del comando e lo stato di ritorno è 0. Comandi separati da a; vengono eseguiti in sequenza; la shell attende che ciascun comando termini a sua volta. Lo stato di ritorno è lo stato di uscita dell'ultimo comando eseguito.
Gli elenchi AND e OR sono sequenze di una o più condutture separate rispettivamente dagli operatori di controllo && e ││. Gli elenchi AND e OR vengono eseguiti con associatività a sinistra.
Un elenco AND ha il formato ...
command1 && command2
Command2 viene eseguito se, e solo se, command1 restituisce uno stato di uscita pari a zero.
Un elenco OR ha il formato ...
command1 ││ command2
Command2 viene eseguito se e solo se command1 restituisce uno stato di uscita diverso da zero.
Lo stato di ritorno degli elenchi AND e OR è lo stato di uscita dell'ultimo comando eseguito nell'elenco.
Tornando al nostro ultimo esempio ...
[[ A == B ]] || echo FALSE && echo TRUE
[[ A == B ]] is false
|| Does NOT mean OR! It means...
'execute next command if last command return code(rc) was false'
echo FALSE The 'echo' command rc is always true
(i.e. it successfully echoed the word "FALSE")
&& Execute next command if last command rc was true
echo TRUE Since the 'echo FALSE' rc was true, then echo "TRUE"
Va bene. Se è corretto, allora perché il prossimo esempio fa eco a qualcosa?
[[ A == A ]] || echo FALSE && echo TRUE
[[ A == A ]] is true
|| execute next command if last command rc was false.
echo FALSE Since last rc was true, shouldn't it have stopped before this?
Nope. Instead, it skips the 'echo FALSE', does not even try to
execute it, and continues looking for a `&&` clause.
&& ... which it finds here
echo TRUE ... so, since `[[ A == A ]]` is true, then it echos "TRUE"
Il rischio di errori logici quando si utilizza più di uno &&
o ||
in un elenco di comandi è piuttosto elevato.
raccomandazioni
Un singolo &&
o ||
in un elenco di comandi funziona come previsto, quindi è abbastanza sicuro. Se è una situazione in cui non è necessaria una clausola else, è possibile seguire qualcosa di simile al seguente (le parentesi graffe sono necessarie per raggruppare gli ultimi 2 comandi) ...
[[ $1 == --help ]] && { echo "$HELP"; exit; }
Molteplici &&
e ||
operatori, in cui ogni comando tranne l'ultimo è un test (cioè tra parentesi [[ ]]
), di solito sono anche sicuri poiché tutti, tranne l'ultimo operatore, si comportano come previsto. L'ultimo operatore si comporta più come una clausola then
o else
.
&&
e||
come incmd1 && cmd2 || cmd3
hanno la stessa precedenza,&&
in((...))
e[[...]]
ha la precedenza su||
(((a || b && c))
is((a || (b && c)))
). Lo stesso vale per-a
/-o
intest
/[
efind
e&
/|
inexpr
.