Differenza tra [[expr1 || expr2]] e [[expr1]] || [[expr2]]


7

Considera due espressioni condizionali expr1e expr2, ad esempio $i -eq $je $k -eq $l. Possiamo scriverlo in bashvari modi. Ecco due possibilità

[[ expr1 || expr2 ]]

[[ expr1 ]] || [[ expr2 ]]

Sono abbastanza sicuro di aver visto qui dei consigli che il secondo dovrebbe essere preferito, ma non riesco a trovare prove a sostegno di questo.

Ecco uno script di esempio che sembra dimostrare che non ci sono differenze:

for i in 0 1
do
  for j in 0 1
  do
    for k in 0 1
    do
      for l in 0 1
      do
        if [[ $i -eq $j || $k -eq $l ]]; then printf "1-yes\t"; else printf "1-no\t"; fi
        if [[ $i -eq $j ]] || [[ $k -eq $l ]]; then printf "2-yes\n"; else printf "2-no\n"; fi
      done
    done
  done
done

e output che mostrano che entrambi i costrutti delle condizioni producono lo stesso risultato:

1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-no    2-no
1-no    2-no
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes
1-yes   2-yes

C'è qualche vantaggio nell'usare un costrutto rispetto all'altro?

Per punti bonus, stessa domanda ma generalizzando a più condizioni usando ||e &&. Ad esempio [[ expr1 && expr2 || expr3 ]],.


Per quanto riguarda il motivo per cui raccomandazioni di questo tipo vengono spesso scritte. [oppure test( non [[ ), cercare i OBtag (collegati alla definizione "obsolescente") nelle specifiche POSIX pertest . In test, ci sono alcuni casi patologici in cui può essere impossibile dire se (o )si intende che la sintassi sia significativa per il comando test o una stringa da testare, quindi le persone che ignorano i marcatori di obsolescenza e usano quella sintassi possono effettivamente aver bisogno del contrario- "x$foo"pratica obsoleta ; con [[, questo non è un problema.
Charles Duffy,

Risposte:


8

Penso che la raccomandazione che hai visto fosse per POSIX sh e / o il testcomando che funge anche da [comando, piuttosto che il [[costrutto che è apparso in ksh (grazie a Stéphane Chazelas per la punta) ed è usato anche per esempio in bash, zsh e altri conchiglie.

Nella maggior parte dei linguaggi, come C, quando una clausola è già nota per essere vera o falsa, non è necessario valutare le parti rimanenti a seconda dell'operazione: se vero non dopo un logico o seguirlo, se falso non dopo un logico e , ecc. Questo ovviamente permette ad esempio di fermarsi quando un puntatore è NULL e non provare a dereferenziarlo nella clausola successiva.

Ma il costrutto di sh[ expr1 -o expr2 ] (inclusa l'implementazione di bash) non lo fa: valuta sempre entrambe le parti, quando si vorrebbe valutare solo expr1 . Ciò potrebbe essere stato fatto per compatibilità con l' testimplementazione del comando. D'altra parte, sh ||e &&segue il solito principio: non valutato se non cambierà il risultato.

Quindi la differenza da notare sarebbe:

: > /tmp/effect #clear effect
if [ 1 -eq 1 -o $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

che produce:

true
or-was-evaluated

Sopra, ognuno [avrebbe potuto essere sostituito con il /usr/bin/[quale è un alias del testcomando, usato prima che [fosse incorporato nelle shell.

Mentre i due successivi costrutti:

: > /tmp/effect #clear effect
if [ 1 -eq 1 ] || [ $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]; then
    echo true;
fi
cat /tmp/effect

o

: > /tmp/effect #clear effect
if [[ 1 -eq 1 || $(echo 1; echo or-was-evaluated > /tmp/effect) -eq 1 ]]; then
    echo true;
fi
cat /tmp/effect

Produrrà truee lascerà solo effectvuoto: ||si comporta correttamente e ha [[corretto anche questo problema.


AGGIORNARE:

Come ha commentato @ StéphaneChazelas, ho perso diverse differenze relative alla domanda iniziale. Metterò qui solo il più importante (almeno per me): priorità degli operatori.

Mentre la shell non considererà la precedenza:

if true || true && false; then
    echo true
else
    echo false
fi

rese (perché non c'è precedenza e quindi prima true || trueviene valutata, quindi && false):

false

all'interno [[ ]]del &&operatore ha la precedenza sul ||:

if [[ 1 -eq 1 || 1 -eq 1 && 1 -eq 0 ]]; then
    echo true
else
    echo false
fi

rendimenti (perché 1 -eq 1 && 1 -eq 0è raggruppato ed è quindi il 2 ° membro di ||):

true

almeno per ksh , bash , zsh .

Quindi [[ ]]ha migliorato il comportamento su entrambi [ ]gli operatori logici diretti e shell.


1
[ x -o y ]non deve valutare entrambe le parti. Mentre GNU testo il [builtin di bashdo, scopriresti strace zsh -c '[ x -o -f /x ]'che [non cerca di stat () /x. Lo stesso con mksh. Ma è vero che -oe -asono gravemente rotto, non possono essere utilizzati in modo affidabile e sono deprecati da POSIX.
Stéphane Chazelas,

1
Una differenza è che all'interno [[...]], &&ha precedenza maggiore ||, mentre all'esterno hanno uguale precedenza. Confronta ksh -c '[[ x || x && "" ]]'vssh -c 'true || true && false'
Stéphane Chazelas il

1
Nota che lo standard [/ testutility non ha un ==operatore. L'operatore di uguaglianza è =(si noti che all'interno di ksh [[...]], =/ ==non sono operatori di uguaglianza, ma di pattern matching).
Stéphane Chazelas,

1
È il contrario. &&ha la precedenza su ||inside [[...]](o ((...))) ma non sugli operatori shell &&e ||. [[ x || y && z ]]is x || (y && z)while x || y && zis (x || y) && z(operatori valutati da sinistra a destra senza precedenza). Simile a come *ha la precedenza su +( 1 + 2 * 3è 1 + (2 * 3)) ma +e -ha la stessa precedenza.
Stéphane Chazelas,

1
Se &&ha la precedenza ||, allora dovrebbe essere true || (true && false), invece di(true || true) && false
Prvt_Yadav,
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.