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
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 medtest
), 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
’ sBASH_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ändasort
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 exempelexpr 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
let
. Det ’ är inte mer standard eller bärbart än(( a = 3 * (2 + 1) ))
(båda kommer frånksh
och finns bara i ksh, bash och zsh) och det ’ är mindre läsbart eller lätt att citera. Använda=$((3 * (2 + 1)))
för att vara bärbar.((a = 3 * (2 + 1) ))
, ett för portabiliteta=$((3 * (2 + 1)))
), så det ’ är inte en anteckning mot dig eller ditt svar utan mot att det är det valda svaret och toppscorer .a=1 $[a+2]
ellera=1 b=2 $[a+b]
. Är deras anledning att undvika den syntaxen?