Operatori logici multipli, ((A || B) && C) e "errore di sintassi vicino token imprevisto"


24

Sto lavorando con Bash 3 e sto cercando di creare un condizionale. In C / C ++, i suoi morti semplice: ((A || B) && C). In Bash, il suo risultato non è così (penso che gli autori di Git debbano aver contribuito a questo codice prima di passare ad altre attività).

Questo non funziona. Nota che <0 or 1>non è una stringa letterale; significa uno 0 o 1 (generalmente viene grep -i).

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eqe "0" ]; then ... fi

Si traduce in:

line 322: syntax error near unexpected token `[['

Ho quindi provato:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ ([ "$A" -eq "0" ]) || ([ "$B" -ne "0" ]) ] && [ "$C" -eq "0" ]; then ... fi

risulta in:

line 322: syntax error near unexpected token `[['

Parte del problema è che i risultati della ricerca sono gli esempi banali e non gli esempi più complessi con condizionali composti.

Come eseguo un semplice ((A || B) && C)in Bash?


Sono pronto a srotolarlo e ripetere gli stessi comandi in più blocchi:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>

if [ "$A" -eq "0" ] && [ "$C" -eq "0" ]; then
    ...
elif [ "$B" -ne "0" ] && [ "$C" -eq "0" ]; then
    ... 
fi

Risposte:


42

La sintassi di bash non è simile a C, anche se una piccola parte è ispirata a C. Non puoi semplicemente provare a scrivere il codice C e aspettarti che funzioni.

Il punto principale di una shell è eseguire comandi. Il comando open- [parent è un comando che esegue un singolo test¹. Puoi persino scriverlo come test(senza la parentesi di chiusura finale). Gli operatori ||e &&sono operatori di shell, combinano comandi , non test.

Quindi quando scrivi

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ]

che è analizzato come

[ [ "$A" -eq "0" ] ||
[ "$B" -ne "0" ] ] &&
[ "$C" -eq "0" ]

che è lo stesso di

test [ "$A" -eq "0" ||
test "$B" -ne "0" ] &&
test "$C" -eq "0"

Notare le parentesi sbilanciate? Sì, non va bene. Il tuo tentativo tra parentesi ha lo stesso problema: parentesi spurie.

La sintassi per raggruppare i comandi è tra parentesi graffe. Il modo in cui le parentesi graffe vengono analizzate richiede un comando completo prima di esse, quindi è necessario terminare il comando all'interno delle parentesi graffe con una nuova riga o punto e virgola.

if { [ "$A" -eq "0" ] || [ "$B" -ne "0" ]; } && [ "$C" -eq "0" ]; then 

C'è un modo alternativo che è quello di usare doppie parentesi. A differenza delle parentesi singole, le doppie parentesi sono una sintassi di shell speciale. Delimitano le espressioni condizionali . All'interno di parentesi doppie, è possibile utilizzare parentesi e operatori come &&e ||. Poiché le parentesi doppie sono sintassi della shell, la shell sa che quando questi operatori si trovano all'interno di parentesi, fanno parte della sintassi dell'espressione condizionale, non parte della normale sintassi del comando shell.

if [[ ($A -eq 0 || $B -ne 0) && $C -eq 0 ]]; then 

Se tutti i tuoi test sono numerici, c'è ancora un altro modo, che delimita le espressioni artihmetiche . Le espressioni aritmetiche eseguono calcoli interi con una sintassi molto simile al C.

if (((A == 0 || B != 0) && C == 0)); then 

Potresti trovare utile il mio primer per staffa bash .

[può essere utilizzato in formato sh. [[e ((sono specifici di bash (e ksh e zsh).

¹ Può anche combinare più test con operatori booleani, ma è complicato da usare e presenta insidie ​​sottili, quindi non lo spiegherò.


Grazie Giles. Questo vale per Bash 2 fino a Bash 4? Ho bisogno di qualcosa di leggermente trasportabile, ma Kusalananda ha menzionato che alcune cose che non stavo facendo sono portatili (ciò implica che ciò che sto facendo non è portatile?). Qui, significa moderatamente da Bash 2 a Bash 4.

@jww è [[ … ]]stato aggiunto in bash 2.02. ((…))è stato aggiunto in bash 2.0.
Gilles 'SO- smetti di essere malvagio' il

@Giles - grazie. Bash 2 è probabilmente ridicolo per la maggior parte. È dovuto al nostro governo e alla riluttanza ad abbandonare le piattaforme più vecchie. Bash 2 e GCC 3 compaiono durante i nostri test Fedora 1. Lasciamo andare una piattaforma più vecchia in occasioni, ma ci devono essere ragioni per questo. Non sottoscriviamo la politica di abbandono di Apple, Microsoft, Mozilla, ecc.

@jww Per favore, comprendi che la precedenza di ||e &&cambia da [a [[e ((. Parità di precedenza in [(utilizzare la parentesi per controllare l'ordine di valutazione), ma && ha una precedenza maggiore in [[e ((rispetto a ||(come in C).

6

Utilizzare [[:

if [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]; then ...

Se preferisci [, le seguenti opere:

if [ \( "$A" -eq "0" -o "$B" -ne "0" \) -a "$C" -eq "0" ]; then ...

3

Usa l' -ooperatore invece del tuo nidificato ||. È inoltre possibile utilizzare -aper sostituire && se necessario nelle altre dichiarazioni.

   EXPRESSION1 -a EXPRESSION2
          both EXPRESSION1 and EXPRESSION2 are true

   EXPRESSION1 -o EXPRESSION2
          either EXPRESSION1 or EXPRESSION2 is true


if [  "$A" -eq "0" -o "$B" -ne "0"  ] && [ "$C" -eq "0" ]; then echo success;fi

Grazie. Mi sono imbattuto nel -ae -o, ma non ho sperimentato con loro. Qualcuno su Stack Overflow ha detto di evitarlo. Per lo più sono d'accordo con loro dal momento che lo script è meno leggibile usandoli.

... ma più portatile.
Kusalananda

@Kusalananda - Grazie per l'heads-up sulla portabilità. Lo script deve essere eseguito su sistemi con Bash 2 tramite Bash 4. Avranno [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]problemi con loro?

Non ci saranno problemi fintanto che manterrai basho qualsiasi altra shell che capisca [[ ... ]].
Kusalananda
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.