Te &&
i ||
operatorzy nie dokładna inline zamienniki do if-then-else. Choć stosowane ostrożnie, mogą osiągnąć to samo.
Pojedynczy test jest prosty i jednoznaczny ...
[[ A == A ]] && echo TRUE # TRUE
[[ A == B ]] && echo TRUE #
[[ A == A ]] || echo FALSE #
[[ A == B ]] || echo FALSE # FALSE
Jednak próba dodania wielu testów może dać nieoczekiwane wyniki ...
[[ 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?)
Dlaczego echo zarówno FAŁSZ, jak i PRAWDA?
To, co się tutaj dzieje, polega na tym, że nie zdawaliśmy sobie z tego sprawy &&
i ||
są przeciążonymi operatorami, które działają inaczej w nawiasach testu warunkowego [[ ]]
niż na liście AND i OR (wykonanie warunkowe), którą tutaj mamy.
Z strony podręcznika bash (edytowane) ...
Listy
Lista jest sekwencją jednego lub więcej potoków oddzielonych przez jednego z operatorów;, &, && lub ││ i opcjonalnie zakończonych przez jeden z;, & lub. Z tych operatorów list, && i ││ mają równy priorytet, a następnie; i &, które mają równy priorytet.
Sekwencja jednego lub więcej znaków nowego wiersza może pojawić się na liście zamiast średnika w celu rozgraniczenia poleceń.
Jeśli polecenie zostanie zakończone przez operatora sterującego &, powłoka wykonuje polecenie w tle w podpowłoce. Powłoka nie czeka na zakończenie polecenia, a zwracanym statusem jest 0. Polecenia oddzielone znakiem a; są wykonywane sekwencyjnie; powłoka czeka na zakończenie każdego polecenia. Status powrotu to status wyjścia ostatniego wykonanego polecenia.
Listy AND i OR są sekwencjami jednego lub więcej potoków oddzielonych odpowiednio przez operatory sterujące && i ││. Listy AND i OR są wykonywane z lewą asocjatywnością.
Lista AND ma postać ...
command1 && command2
Polecenie2 jest wykonywane wtedy i tylko wtedy, gdy polecenie1 zwraca status wyjścia równy zero.
Lista OR ma postać ...
command1 ││ command2
Komenda2 jest wykonywana tylko wtedy, gdy komenda1 zwraca niezerowy status wyjścia.
Status powrotu list AND i OR to status wyjścia ostatniego polecenia wykonanego na liście.
Wracając do naszego ostatniego przykładu ...
[[ 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"
W porządku. Jeśli to prawda, to dlaczego w ogóle przedostatni przykład w ogóle coś przypomina?
[[ 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"
Ryzyko błędów logicznych podczas używania więcej niż jednego &&
lub ||
na liście poleceń jest dość wysokie.
Rekomendacje
Pojedynczy &&
lub ||
na liście poleceń działa zgodnie z oczekiwaniami, więc jest całkiem bezpieczny. Jeśli jest to sytuacja, w której nie potrzebujesz klauzuli else, łatwiej jest zastosować coś takiego jak następujące (nawiasy klamrowe są wymagane do zgrupowania ostatnich 2 poleceń) ...
[[ $1 == --help ]] && { echo "$HELP"; exit; }
Wiele operatorów &&
i ||
operatory, w których każde polecenie oprócz ostatniego jest testem (tj. W nawiasach [[ ]]
), są zwykle również bezpieczne, ponieważ wszyscy operatorzy oprócz ostatniego zachowują się zgodnie z oczekiwaniami. Ostatni operator działa bardziej jak klauzula then
lub else
.
&&
i||
powłoki jak w incmd1 && cmd2 || cmd3
mają taki sam priorytet,&&
in((...))
i[[...]]
mają pierwszeństwo przed||
(((a || b && c))
is((a || (b && c)))
). To samo dotyczy-a
/-o
wtest
/[
afind
i&
/|
wexpr
.