Flere logiske operatorer, ((A || B) & & C) og “ syntaksfejl nær uventet token ”

Jeg arbejder med Bash 3, og jeg prøver at danne en betinget. I C / C ++ er det dødt simpelt: ((A || B) && C). I Bash viser det sig ikke sådan (jeg tror, at Git-forfatterne må have bidraget med denne kode, før de gik videre til andre bestræbelser).

Dette fungerer ikke. Bemærk, at <0 or 1> ikke er en streng bogstavelig; det betyder en 0 eller 1 (kommer normalt fra 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 

Det resulterer i:

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

Jeg prøvede derefter:

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

det resulterer i:

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

En del af problemet er søgeresultaterne er de trivielle eksempler og ikke de mere komplekse eksempler med sammensatte betingelser.

Hvordan udfører jeg et simpelt ((A || B) && C) i Bash?


Jeg er klar til bare at rulle den ud og gentage de samme kommandoer i flere blokke:

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 

Svar

Syntaksen for bash er ikke C-lignende, selvom en lille del af det er inspireret af C. Du kan ikke bare prøve at skrive C-kode og forvente, at den fungerer.

Hovedpunktet i en shell er at køre kommandoer. Kommandoen med åben parentes [ er en kommando, der udfører en enkelt test¹. Du kan endda skrive det som test (uden den sidste lukkebeslag). || og && operatorer er shelloperatorer, de kombinerer kommandoer og ikke tests.

Så når du skriver

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

at “parses som

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

hvilket er det samme som

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

Bemærk de ubalancerede parenteser? Ja, det er ikke godt. Dit forsøg med parenteser har det samme problem: falske parenteser.

Syntaksen til gruppekommandoer sammen er parenteser. Den måde, hvorpå parenteser analyseres, kræver en komplet kommando foran dem, så du bliver nødt til at afslutte kommandoen inde i selerne med en ny linje eller semikolon.

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

“er en alternativ måde at bruge dobbelt parentes. I modsætning til enkelte parenteser er dobbelte parenteser speciel shell-syntaks. De afgrænser betingede udtryk . Inde i dobbelte parenteser kan du bruge parenteser og operatorer som && og ||. Da de dobbelte parenteser er shell-syntaks, ved shell, at når disse operatorer er inden for parenteser, er de “en del af den betingede udtrykssyntaks, ikke en del af den almindelige shell-kommandosyntaks.

Hvis alle dine tests er numeriske, er der endnu en anden måde, der afgrænser artihmetiske udtryk . Aritmetiske udtryk udfører heltalberegninger med en meget C-lignende syntaks.

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

Du kan finde min bash-beslag primer nyttigt.

[ kan bruges i almindelig sh. [[ og (( er specifikke for bash (og ksh og zsh).

¹ Det kan også kombinere flere tests med boolske operatører, men dette er besværligt at bruge og har subtile faldgruber, så jeg forklarer det ikke.

Kommentarer

  • Tak Giles.Holder dette for Bash 2 gennem Bash 4? Jeg har brug for noget mildt bærbarhed, men Kusalananda nævnte nogle ting, jeg ikke gjorde, er bærbare (betyder det, hvad jeg laver, er ikke bærbar?). Her betyder mildt Bash 2 til Bash 4.
  • @jww [[ … ]] blev tilføjet i bash 2.02. ((…)) blev tilføjet i bash 2.0.
  • @Giles – tak. Bash 2 er sandsynligvis latterligt for de fleste. Det skyldes vores regeringsførelse og uvillighed at opgive ældre platforme. Bash 2 og GCC 3 vises på vores Fedora 1-test. Vi vil lade en ældre platform gå lejlighedsvis, men der skal være grunde til det. Vi don ‘ t abonnerer på Apple, Microsoft, Mozilla osv. politik for nedlæggelse.
  • @jww Du skal forstå, at forrang for || og && skift fra [ til [[ og ((. Lige forrang i [ (brug parentes til at kontrollere rækkefølgen af evalueringen), men & & har højere prioritet i [[ og (( end || (som i C).
  • @Roland Nej, parenteser inde i [[ … ]] eller $((…)) er kun til gruppering.

Svar

Brug [[:

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

Hvis du foretrækker [, fungerer følgende:

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

Kommentarer

  • Tak, Stephen. I mit tilfælde brugte jeg dit første eksempel således … regex = ‘ ^ [0-9] + $ ‘ length = $ {#datearg} hvis! [[$ datearg = ~ $ regex & & $ længde -ekv 8]]; derefter ekko ” fejl: Ikke et antal lenth af 8 “; fi

Svar

Brug -o operatoren i stedet for din indlejret ||. Du kan også bruge -a til at erstatte & & hvis det er nødvendigt i dine andre udsagn .

 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 

Kommentarer

  • Tak. Jeg stødte på -a og -o, men jeg eksperimenterede ikke med dem. En person på Stack Overflow sagde, at han skulle undgå det. Jeg var for det meste enig med dem, da scriptet er mindre læsbart ved hjælp af dem.
  • … men mere bærbare.
  • @Kusalananda – Tak for heads-ups til bærbarhed. Scriptet skal køre på systemer med Bash 2 til Bash 4. Har [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]] problemer med dem?
  • Der er ingen problemer, så længe du holder bash eller enhver anden skal, der forstår [[ ... ]].
  • Undersiden af at bruge -a er, at den ikke ‘ t kortslutning, hvis det første udtryk er falsk. Så hvis du stoler på kortslutning for at forhindre, at et potentielt fatalt 2. udtryk kører, kan du ikke bruge -a. Eksempel: pastebin.com/zM77TXW1

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *