Parentes i expr-aritmetik: 3 * (2 + 1) (Svenska)

expr verkar inte gilla parentes (används i matematik till uttrycklig operatörprioritet):

expr 3 * (2 + 1) bash: syntax error near unexpected token `(" 

Hur uttrycker man operatörsprioritet i bash?

Svar

Ett annat sätt att använda let bash-inbyggt:

$ let a="3 * (2 + 1)" $ printf "%s\n" "$a" 9 

Obs

Som @ Stéphane Chazelas påpekade , i bash ska du använda ((...)) för att göra aritmetik över expr eller let för läsbarhet.

För bärbarhet, använd $((...)) som @Bernhard svar .

Kommentarer

  • +1 Ännu mer läsbar! Jag lade upp min fråga + svar och tänkte bara att det skulle vara till hjälp för mina andra Linux-användare, men nu får jag mycket nytta av de andra svaren 🙂
  • Där ’ är ingen anledning att använda let. Det ’ är inte mer standard eller bärbart än (( a = 3 * (2 + 1) )) (båda kommer från ksh och finns bara i ksh, bash och zsh) och det ’ är mindre läsbart eller lätt att citera. Använd a=$((3 * (2 + 1))) för att vara bärbar.
  • Jag ’ jag säger inte det ’ är fel, jag ’ jag säger bara att det inte ska användas eftersom det finns bättre alternativ (ett för läsbarhet ((a = 3 * (2 + 1) )), ett för portabilitet a=$((3 * (2 + 1)))), så det ’ är inte en anteckning mot dig eller ditt svar utan mot att det är det valda svaret och toppscorer .
  • @St é phaneChazelas: Uppdaterat mitt svar!
  • Jag har alltid använt a=1 $[a+2] eller a=1 b=2 $[a+b]. Är deras anledning att undvika den syntaxen?

Svar

Du kan istället använda den aritmetiska expansionen.

echo "$(( 3 * ( 2 + 1 ) ))" 9 

Enligt min personliga åsikt ser det lite trevligare ut än att använda expr.

Från man bash

Aritmetisk expansion Aritmetisk expansion möjliggör utvärdering av ett aritmetiskt uttryck och ersättning av resultatet. Formatet för aritmetisk expansion är:

 $((expression)) 

Uttrycket behandlas som om det var inom dubbla citat, men ett dubbelt citat inom parentes behandlas inte speciellt. Alla tokens i uttrycket genomgår parameterutvidgning, strängutvidgning, kommandosubstitution och borttagning av offert. Aritmetiska utvidgningar kan vara kapslade.

Utvärderingen utförs enligt reglerna nedan under ARITMETISK UTVÄRDERING. Om uttrycket är ogiltigt skriver bash ut ett meddelande som indikerar misslyckande och ingen ersättning inträffar.

Kommentarer

  • Bortsett från läsbarheten, kräver det inte ’ att man gafflar en extra process för att göra aritmetiken; det ’ hanteras av själva skalet.
  • Observera att i POSIX-skal är det ’ ordet splittring, så det är ’ en bra vana att citera det i listkontexter.
  • När jag prövar detta i bash-skalet får jag ’ Olagligt variabelnamn. ”

Svar

Det finns ingen anledning att använda expr för aritmetik i moderna skal.

POSIX definierar $((...)) expansionsoperatör. Så du kan använda det i alla POSIX-kompatibla skal (sh av alla moderna Unix-likes, dash, bash, yash, mksh, zsh, posh, ksh … ).

a=$(( 3 * (2 + 1) )) a=$((3*(2+1))) 

ksh introducerade också en let inbyggd får samma typ av aritmetiska uttryck, expanderar inte till något men returnerar en utgångsstatus baserat på om uttrycket resolv es till 0 eller inte, som i expr:

if let "a = 3 * (2 + 1)"; then echo "$a is non-zero" fi 

Eftersom citatet gör det dock besvärligt och inte mycket läsbart (inte i samma utsträckning som expr förstås), ksh introducerade också en ((...)) alternativ form:

if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then echo "$a is non-zero and 3 > 1" fi ((a+=2)) 

vilket är mycket mer läsbart och bör användas istället.

let och ((...)) är endast tillgängliga i ksh, zsh och bash.$((...)) syntax bör föredras om portabilitet till andra skal behövs. expr behövs bara för pre-POSIX Bourne-liknande skal (vanligtvis Bourne-skalet eller tidiga versioner av Almquist-skalet.

På den icke-Bourne-fronten finns det några skal med inbyggd aritmetisk operator:

  • csh / tcsh (faktiskt det första Unix-skalet med inbyggd aritmetisk utvärdering):

    @ a = 3 * (2 + 1) 
  • akanga (baserat på rc)

    a = $:"3 * (2 + 1)" 
  • som en historikanteckning, den ursprungliga versionen av Almquist-skalet, som publicerades på usenet 1989, hade en expr inbyggd (faktiskt slogs samman med test), men den togs bort senare.

Kommentarer

  • Jag lär mig något nytt varje dag av dig, St é phane. Jag uppskattar mycket din kunskap om POSIX-skal!
  • Vad sägs om : $((a = a*2))?
  • Vad händer om jag har en flytande punkt? Mitt uttryck är a = $ ((-14 + 0,2 * (1 + 2 + 3)). Feltoken är ” .2 * (1 + 2 + 3) ”
  • @Blaise, då kommer du ’ d behöver ett skal som stöder flytande punkter i $((...)) som zsh, ksh93 eller yash.

Svar

expr är ett externt kommando, det är ingen speciell skal-syntax. Därför, om du vill att expr ska se specialtecken för skal, måste du skydda dem från skalparsning genom att citera dem. Dessutom behöver expr varje nummer och operatör skickas som en separat parameter. Således:

expr 3 \* \( 2 + 1 \) 

Om du inte arbetar med ett antikt unix-system från 1970- eller 1980-talet, finns det mycket liten anledning att använda expr. Förr i tiden hade skalen inte ett inbyggt sätt att utföra aritmetik, och du var tvungen att ringa expr i stället. Alla POSIX-skal har inbyggd aritmetik via aritmetisk expansion syntax.

echo "$((3 * (2 + 1)))" 

The konstruktion $((…)) expanderar till resultatet av det aritmetiska uttrycket (skrivet i decimal). Bash, som de flesta skal, stöder endast heltal aritmetisk modulo 2 64 (eller modulo 2 32 för äldre versioner av bash och vissa andra skal på 32-bitars maskiner).

Bash erbjuder en ytterligare bekvämlighetssyntax när du vill utföra uppgifter eller testa om ett uttryck är 0 men bryr dig inte om resultatet. Denna konstruktion finns också i ksh och zsh men inte i vanlig sh.

((x = 3 * (2+1))) echo "$x" if ((x > 3)); then … 

Förutom heltal aritmetik, expr erbjuder några strängmanipulationsfunktioner. Dessa ingår också av funktioner i POSIX-skal, förutom en: expr STRING : REGEXP testar om strängen matchar den angivna regex. Ett POSIX-skal kan inte göra detta utan externa verktyg, men bash kan med [[ STRING =~ REGEXP ]] (med en annan regexp-syntax expr är ett klassiskt verktyg och använder BRE, bash använder ERE).

Om du inte underhåller skript som körs på 20-åriga system behöver du inte veta att expr någonsin existerat. Använd skalaritmetik.

Kommentarer

  • expr foo : '\(.\)' gör även text extrahering. bash ’ s BASH_REMATCH uppnår något liknande. Det gör också strängjämförelse, vilket POSIX [ inte gör (även om man kan tänka sig sätt att använda sort för det).
  • betona som platshållare för syntax — du är en Schemer, @Giles? :]
  • @RubyTuesdayDONO Jag använde inte ’ här. Felläser du U + 2026 HORIZONTAL ELLIPSIS? Om så är fallet, försök använda ett större teckensnitt.
  • @Giles – okej, ja, det ser bara ut som en understrykning på grund av min teckenstorlek. för mig är ” Schemer ” ett komplement, och det ’ är inte som ellips kontra understrykning ändrar meningen ändå … inget behov av att bli snarky över det: /

Svar

Använd parentes med citat:

expr 3 "*" "(" 2 "+" 1 ")" 9 

Citaten hindrar bash från att tolka parentesen som bas-syntax.

Kommentarer

  • Det Nicolas illustrerar men inte förklarar är att symbolerna på expr kommandoraden måste separeras med mellanslag; så; till exempel expr 3 "*" "(2" "+" "1)" fungerar inte . (BTW, du behöver antagligen inte citera +.)
  • Parenteserna är inte ’ t nyckelord som while och [[, de ’ är syntax. Om det var nyckelord skulle de inte ’ tolkas som sådana i kommandot. Du behöver citat så att bash inte ’ tolkar dem utan istället ser en sträng bokstavlig.

Svar

Om du har bc ..

echo "3 * (2 + 1)"|bc 9 

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *