Nawias w wyrażeniu arytmetycznym: 3 * (2 + 1)


59

expr wydaje się nie lubić nawiasów (używanych w matematyce do jawnego priorytetu operatora):

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

Jak wyrazić priorytet operatora w bash?

Odpowiedzi:


40

Innym sposobem na użycie letwbudowanego basha:

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

Uwaga

Jak zauważył @ Stéphane Chazelas , bashpowinieneś używać ((...))arytmetyki nad exprlub letdla czytelności.

Aby uzyskać przenośność, użyj odpowiedzi$((...)) typu @Bernhard .


1
+1 Jeszcze bardziej czytelne! Wysłałem moje pytanie + odpowiedź, myśląc, że byłoby to pomocne dla moich innych użytkowników Linuksa, ale teraz czerpię wiele korzyści z innych odpowiedzi :-)
Nicolas Raoul

3
Nie ma powodu do korzystania let. Nie jest bardziej standardowy ani przenośny niż (( a = 3 * (2 + 1) ))(oba pochodzą z kshi są dostępne tylko w ksh, bash i zsh) i jest mniej czytelny lub łatwy do cytowania. Użyj, a=$((3 * (2 + 1)))aby być przenośnym.
Stéphane Chazelas,

2
Nie twierdzę, że jest źle, po prostu mówię, że nie należy go stosować, ponieważ istnieją lepsze alternatywy (jedna dla czytelności ((a = 3 * (2 + 1) )), jedna dla przenośności a=$((3 * (2 + 1)))), więc nie jest to notatka przeciwko tobie lub twojej odpowiedzi, ale przeciwko temu, że jest wybraną odpowiedzią i najlepszy strzelec.
Stéphane Chazelas,

@ StéphaneChazelas: Zaktualizowałem moją odpowiedź!
cuonglm

Zawsze używałem a=1 $[a+2]lub a=1 b=2 $[a+b]. Czy ich powodem jest unikanie tej składni?
Gordon

73

Zamiast tego możesz użyć rozszerzenia arytmetycznego.

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

Moim osobistym zdaniem wygląda to nieco ładniej niż używanie expr.

Od man bash

Rozwinięcie arytmetyczne Rozwinięcie arytmetyczne pozwala na ocenę wyrażenia arytmetycznego i podstawienie wyniku. Format rozszerzania arytmetycznego to:

         $((expression))

Wyrażenie jest traktowane tak, jakby znajdowało się w podwójnych cudzysłowach, ale podwójne cudzysłowy w nawiasach nie są traktowane specjalnie. Wszystkie tokeny w wyrażeniu podlegają interpretacji parametrów, interpretacji ciągów, zastępowaniu poleceń i usuwaniu cytatów. Rozszerzenia arytmetyczne mogą być zagnieżdżone.

Ocena przeprowadzana jest zgodnie z zasadami wymienionymi poniżej w części OCENA ARTYMETYCZNA. Jeśli wyrażenie jest niepoprawne, bash wypisuje komunikat wskazujący błąd i nie występuje podstawienie.


1
Oprócz czytelności nie wymaga również wykonania dodatkowego procesu arytmetycznego; jest obsługiwany przez samą powłokę.
chepner

Zauważ, że w powłokach POSIX jest ono podzielone na słowa, więc dobrym zwyczajem jest cytowanie go w kontekście list.
Stéphane Chazelas,

Kiedy próbuję tego w powłoce bash, otrzymuję „Nielegalną nazwę zmiennej”.
lordhog

40

Nie ma powodu, aby używać exprarytmetyki w nowoczesnych powłokach.

POSIX definiuje $((...))operator ekspansji. Możesz więc używać tego we wszystkich powłokach zgodnych z POSIX ( shwszystkich współczesnych uniksowych lajków, dash, bash, yash, mksh, zsh, posh, ksh ...).

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

kshwprowadzono również letwbudowane, które jest przekazywane tego samego rodzaju wyrażenie arytmetyczne, nie rozwija się w coś, ale zwraca status wyjścia na podstawie tego, czy wyrażenie jest interpretowane na 0, czy nie, jak w expr:

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

Ponieważ jednak cytowanie sprawia, że ​​jest to niewygodne i mało czytelne (nie w takim samym stopniu jak exproczywiście), kshwprowadzono także ((...))alternatywną formę:

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

który jest o wiele bardziej czytelny i powinien być używany zamiast tego.

leti ((...))są dostępne tylko w ksh, zshi bash. $((...))Składni powinny być korzystna, jeśli potrzebny do przenoszenia innych powłok, exprpotrzebne jest tylko do wstępnego POSIX Bourne powłokach (zazwyczaj Bourne powłoki lub wczesne wersje Almquist Shell).

Na froncie innym niż Bourne znajduje się kilka pocisków z wbudowanym operatorem arytmetycznym:

  • csh/ tcsh(właściwie pierwsza powłoka uniksowa z wbudowaną analizą arytmetyczną):

    @ a = 3 * (2 + 1)
  • akanga(na podstawie rc)

    a = $:'3 * (2 + 1)'
  • jak napisano w historii, oryginalna wersja powłoki Almquist, opublikowana na usenet w 1989 roku, miała exprwbudowaną (faktycznie połączoną test), ale została później usunięta.


Codziennie uczę się od ciebie czegoś nowego, Stéphane. Bardzo doceniam twoją wiedzę na temat powłok POSIX!
MattBianco,

Jak o : $((a = a*2))?
Arthur2e5

Co jeśli mam zmiennoprzecinkowy? Moje wyrażenie to a = $ ((-14 + 0,2 * (1 + 2 + 3))). Token błędu to „.2 * (1 + 2 + 3)”
Blaise

@ Blaise, wtedy potrzebujesz powłoki obsługującej zmiennoprzecinkowe, $((...))takie jak zsh, ksh93 lub yash.
Stéphane Chazelas

16

exprjest poleceniem zewnętrznym, nie jest specjalną składnią powłoki. Dlatego jeśli chcesz exprzobaczyć znaki specjalne powłoki, musisz chronić je przed analizą powłoki, cytując je. Ponadto exprwymaga podania każdej liczby i operatora jako osobnego parametru. A zatem:

expr 3 \* \( 2 + 1 \)

O ile nie pracujesz na antycznym systemie uniksowym z lat 70. lub 80. XX wieku, nie ma zbyt wiele powodów, aby z niego korzystać expr. W dawnych czasach powłoki nie miały wbudowanego sposobu wykonywania arytmetyki, a exprzamiast tego trzeba było wywołać narzędzie. Wszystkie powłoki POSIX mają wbudowaną arytmetykę poprzez arytmetyczną składnię rozszerzającą .

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

Konstrukt $((…))rozwija się do wyniku wyrażenia arytmetycznego (zapisanego dziesiętnie). Bash, podobnie jak większość powłok, obsługuje tylko liczbę całkowitą arytmetyczną modulo 2 64 (lub modulo 2 32 dla starszych wersji basha i niektórych innych powłok na maszynach 32-bitowych).

Bash oferuje dodatkową wygodną składnię, gdy chcesz wykonać przypisania lub sprawdzić, czy wyrażenie ma wartość 0, ale nie przejmuj się wynikiem. Ta konstrukcja istnieje również w ksh i zsh, ale nie w zwykłym sh.

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

Oprócz arytmetyki liczb całkowitych exproferuje kilka funkcji manipulacji ciągami. Te także są uwzględniane przez funkcje powłok POSIX, z wyjątkiem jednego: expr STRING : REGEXPtestuje, czy łańcuch pasuje do określonego wyrażenia regularnego. Powłoka POSIX nie może tego zrobić bez zewnętrznych narzędzi, ale bash może [[ STRING =~ REGEXP ]](z inną składnią wyrażenia regularnegoexprjest klasycznym narzędziem i używa BRE, bash używa ERE).

O ile nie zajmujesz się skryptami działającymi na 20-letnich systemach, nie musisz wiedzieć, że exprkiedykolwiek istniał. Użyj arytmetyki powłoki.


expr foo : '\(.\)'wykonuje również ekstrakcję tekstu. bash„s BASH_REMATCHosiąga coś podobnego. Dokonuje również porównania ciągów, czego [nie robi POSIX (choć można sobie wyobrazić, jak tego użyć sort).
Stéphane Chazelas

podkreślenie jako symbol zastępczy składni --- jesteś Schemerem, @Giles? :]
RubyT TuesdayDONO,

1
@RubyT TuesdayDONO Nie użyłem tutaj podkreślenia. Czy źle czytasz U + 2026 HORIZONTAL ELLIPSIS? Jeśli tak, spróbuj użyć większej czcionki.
Gilles,

@Giles - okej, tak, wygląda tylko na podkreślenie ze względu na mój rozmiar czcionki. dla mnie „Schemer” jest uzupełnieniem i nie jest tak, że elipsa kontra podkreślenie i tak zmienia znaczenie… nie trzeba się w to
wstydzić

12

Używaj nawiasów z cytatami:

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

Cytaty uniemożliwiają bashowi zinterpretowanie nawiasu jako składni bash.


2
Nicolas ilustruje, ale nie wyjaśnia, że ​​tokeny w exprwierszu poleceń muszą być oddzielone spacjami; więc; na przykład expr 3 "*" "(2" "+" "1)" nie będzie działać . (Ponadto, BTW, prawdopodobnie nie musisz cytować +.)
G-Man,

Nawiasy nie są słowami kluczowymi podobnymi do, whilei [[są składnią. Gdyby były słowami kluczowymi, nie byłyby interpretowane jako takie w argumentach poleceń. Potrzebujesz cudzysłowów, aby bash ich nie analizował, a zamiast tego widział dosłowny ciąg znaków.
Gilles

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.