Operatori logici multipli, ((A || B) & & C) și “ eroare de sintaxă lângă simbolul neașteptat ”

Lucrez cu Bash 3 și încerc să formează un condițional. În C / C ++, este simplu: ((A || B) && C). În Bash, nu se dovedește așa (cred că autorii Git trebuie să fi contribuit cu acest cod înainte de a trece la alte eforturi).

Acest lucru nu funcționează. Rețineți că <0 or 1> nu este un șir literal; înseamnă un 0 sau 1 (în general provine de la 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 

Rezultă în:

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

Am încercat apoi:

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

are ca rezultat:

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

O parte a problemei este că rezultatele căutării sunt exemplele banale și nu exemplele mai complexe cu condiționali compuși.

Cum pot efectua un simplu ((A || B) && C) în Bash?


Sunt „gata să îl derulez și să repet aceleași comenzi în mai multe blocuri:

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 

Răspuns

Sintaxa bash nu este asemănătoare cu C, chiar dacă o mică parte din aceasta este inspirată de C. Nu puteți încerca pur și simplu să scrieți cod C și să vă așteptați să funcționeze.

Punctul principal al unui shell este să executați comenzi. Comanda open-bracket [ este o comandă, care efectuează un singur test¹. Puteți chiar să-l scrieți ca test (fără paranteză finală de închidere). Operatorii || și && sunt operatori shell, combină comenzi , nu teste.

Deci, atunci când scrieți

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

asta este analizat ca

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

care este la fel ca

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

Observați parantezele dezechilibrate? Da, nu este bine. Încercarea dvs. cu paranteze are aceeași problemă: paranteze false.

Sintaxa pentru a grupa comenzile împreună este paranteze. Modul în care sunt analizate parantezele necesită o comandă completă înainte de acestea, așa că va trebui să terminați comanda din interiorul parantezelor cu o linie nouă sau punct și virgulă.

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

Acolo „este o modalitate alternativă prin care se utilizează paranteze duble. Spre deosebire de parantezele simple, parantezele duble sunt sintaxa specială a shell-ului. Acestea delimitează expresii condiționale . În paranteze duble, puteți folosi paranteze și operatori precum && și ||. Deoarece parantezele duble sunt sintaxa shell-ului, shell-ul știe că atunci când acești operatori se află în paranteze, acestea fac parte din sintaxa expresiei condiționate, nu fac parte din sintaxa ordonată a comenzii shell.

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

Dacă toate testele dvs. sunt numerice, există încă un alt mod, care delimitează expresii artihmetice . Expresiile aritmetice efectuează calcule întregi cu o sintaxă foarte asemănătoare cu C.

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

Puteți găsi parantezul meu bash primer util.

[ poate fi folosit în sh simplu. [[ și (( sunt specifice bash (și ksh și zsh).

¹ combinați mai multe teste cu operatori booleeni, dar acest lucru este dificil de utilizat și are capcane subtile, așa că nu l-am explicat.

Comentarii

  • Mulțumesc, Giles. Este valabil pentru Bash 2 până la Bash 4? Am nevoie de ceva ușor de portabilitate, dar Kusalananda a menționat că unele lucruri pe care nu le fac sunt portabile (asta înseamnă că ceea ce fac este nu portabil?). Aici, ușor înseamnă Bash 2 până la Bash 4.
  • a fost adăugat @jww [[ … ]] în bash 2.02. ((…)) a fost adăugat în bash 2.0.
  • @Giles – mulțumesc. Bash 2 este probabil de râs celor mai mulți. Se datorează guvernanței noastre și refuzului nostru să abandonăm platformele mai vechi. Bash 2 și GCC 3 apar la testarea Fedora 1. Vom lăsa o platformă mai veche să meargă ocazional, dar trebuie să existe motive pentru aceasta. Nu ' nu vă abonați la politica Apple, Microsoft, Mozilla etc. a abandonware-ului.
  • @jww Vă rugăm să înțelegeți că precedența || și && schimbați de la [ la [[ și ((. prioritate egală în [ (utilizați paranteze pentru a controla ordinea evaluării), dar & & are o prioritate mai mare în [[ și (( decât || (ca în C).
  • @Roland Nu, paranteze în interiorul [[ … ]] sau $((…)) sunt doar pentru grupare.

Răspuns

Utilizați [[:

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

Dacă preferați [, funcționează următoarele:

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

Comentarii

  • Mulțumesc, Stephen. În cazul meu, am folosit primul dvs. exemplu astfel … regex = ' ^ [0-9] + $ ' length = $ {#datearg} dacă! [[$ datearg = ~ $ regex & & $ length -eq 8]]; apoi echo " eroare: Nu un număr de lenth din 8 "; fi

Răspuns

Utilizați operatorul -o în locul dvs. imbricat ||. De asemenea, puteți utiliza -a pentru a înlocui & & dacă este necesar în celelalte afirmații .

 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 

Comentarii

  • Mulțumesc. Am dat peste -a și -o, dar nu am experimentat cu ele. Cineva din Stack Overflow a spus să o evite. Majoritatea am fost de acord cu ei, deoarece scriptul este mai puțin lizibil folosindu-le.
  • … dar mai portabil.
  • @Kusalananda – Vă mulțumim pentru head-up-uri despre portabilitate. Scriptul trebuie să ruleze pe sistemele cu Bash 2 până la Bash 4. Va avea [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]] probleme cu ele?
  • Nu vor exista probleme atâta timp cât veți continua cu bash sau orice alt shell care înțelege [[ ... ]].
  • Partea de jos a utilizării -a este că nu ' t scurtcircuit dacă prima expresie este falsă. Deci, dacă vă bazați pe scurtcircuit pentru a preveni executarea unei a doua expresii fatale, atunci nu puteți utiliza -a. Exemplu: pastebin.com/zM77TXW1

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *