Круглая скобка в выраженной арифметике: 3 * (2 + 1)

60

expr не похоже на круглые скобки (используется в математике для явного приоритета оператора):

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

Как выразить приоритет оператора в bash?

Николас Рауль
источник

Ответы:

40

Еще один способ использовать letвстроенную Bash:

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

Запись

Как отметил @ Stéphane Chazelas , bashвы должны использовать, ((...))чтобы делать арифметику над exprили letдля удобочитаемости.

Для переносимости используйте ответ$((...)) типа @Bernhard .

cuonglm
источник
1
+1 Еще более читабельно! Я разместил свой вопрос + ответ, просто думая, что это будет полезно для моих коллег-пользователей Linux, но теперь я получаю много пользы от других ответов :-)
Николас Рауль
3
Там нет причин, чтобы использовать let. Он не является более стандартным или переносимым, чем (( a = 3 * (2 + 1) ))(оба получены kshи доступны только в ksh, bash и zsh), и его менее разборчиво или легко процитировать. Используйте, a=$((3 * (2 + 1)))чтобы быть портативным.
Стефан Шазелас
2
Я не говорю, что это неправильно, я просто говорю, что его не следует использовать, поскольку есть лучшие альтернативы (одна для удобочитаемости ((a = 3 * (2 + 1) )), другая для переносимости a=$((3 * (2 + 1)))), так что это не примечание против вас или вашего ответа, а против того, чтобы он был выбранным ответом и лучший бомбардир.
Стефан Шазелас
@ StéphaneChazelas: обновил мой ответ!
cuonglm
Я всегда использовал a=1 $[a+2]или a=1 b=2 $[a+b]. Их причина, чтобы избежать этого синтаксиса?
Гордон
74

Вместо этого вы можете использовать арифметическое расширение.

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

По моему личному мнению, это выглядит немного лучше, чем использование expr.

Из man bash

Арифметическое расширение Арифметическое расширение позволяет оценить арифметическое выражение и заменить результат. Формат для арифметического расширения:

         $((expression))

Выражение обрабатывается так, как если бы оно было в двойных кавычках, но двойные кавычки в скобках специально не обрабатываются. Все токены в выражении подвергаются раскрытию параметров, расширению строк, подстановке команд и удалению кавычек. Арифметические разложения могут быть вложенными.

Оценка проводится в соответствии с правилами, перечисленными ниже в разделе АРИФМЕТИЧЕСКАЯ ОЦЕНКА. Если выражение недопустимо, bash печатает сообщение, указывающее сбой, и подстановка не происходит.

Бернхард
источник
1
Помимо читабельности, он также не требует разветвления дополнительного процесса для выполнения арифметики; это обрабатывается самой оболочкой.
chepner
Обратите внимание, что в оболочках POSIX он разделяется на слова, поэтому полезно указывать его в списках.
Стефан Шазелас
Когда я пытаюсь сделать это в оболочке bash, я получаю «Недопустимое имя переменной».
lordhog
40

Нет смысла использовать exprарифметику в современных оболочках.

POSIX определяет $((...))оператор расширения. Таким образом, вы можете использовать это во всех POSIX-совместимых оболочках ( shвсех современных Unix-лайков, dash, bash, yash, mksh, zsh, posh, ksh ...).

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

kshтакже представил letвстроенную функцию, которой передается то же самое арифметическое выражение, не расширяется во что-то, но возвращается состояние выхода, основанное на том, разрешает ли выражение значение 0 или нет, как в expr:

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

Однако, поскольку цитирование делает его неловким и не очень разборчивым (не в такой же степени, как, exprконечно), kshтакже введена ((...))альтернативная форма:

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

который намного более разборчивый и должен использоваться вместо этого.

letи ((...))доступны только в ksh, zshи bash. $((...))Синтаксис должен быть предпочтительным , если портативность для других оболочек необходима, exprтребуется только для предварительного POSIX Борн-подобных оболочек (обычно Bourne оболочки или ранних версий Almquist оболочки).

На фронте не-Борна есть несколько оболочек со встроенным арифметическим оператором:

  • csh/ tcsh(фактически первая оболочка Unix со встроенной арифметической оценкой):

    @ a = 3 * (2 + 1)
  • akanga(на основании rc)

    a = $:'3 * (2 + 1)'
  • Как следует из истории, оригинальная версия оболочки Almquist, опубликованная в usenet в 1989 году, имела exprвстроенную функцию (фактически объединенную с test), но позже была удалена.

Стефан Шазелас
источник
Я каждый день узнаю что-то новое от тебя, Стефан. Я очень ценю ваши знания оболочки POSIX!
MattBianco
Как насчет : $((a = a*2))?
Arthur2e5
Что если у меня есть плавающая точка? Мое выражение a = $ ((-14 + 0,2 * (1 + 2 + 3))). Токен ошибки «.2 * (1 + 2 + 3)»
Блейз,
@Blaise, тогда вам понадобится оболочка, которая поддерживает плавающие точки, $((...))такие как zsh, ksh93 или yash.
Стефан Шазелас
16

exprэто внешняя команда, это не специальный синтаксис оболочки. Поэтому, если вы хотите exprвидеть специальные символы оболочки, вам нужно защитить их от разбора оболочки, заключив их в кавычки. Кроме того, exprнеобходимо, чтобы каждый номер и оператор передавались как отдельный параметр. Таким образом:

expr 3 \* \( 2 + 1 \)

Если вы не работаете над антикварной системой Unix 1970-х или 1980-х годов, причин для использования очень мало expr. В старые времена в оболочках не было встроенного способа выполнения арифметики, и exprвместо этого приходилось вызывать утилиту. Все оболочки POSIX имеют встроенную арифметику через синтаксис арифметического расширения .

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

Конструкция $((…))расширяется до результата арифметического выражения (записанного в десятичной форме). Bash, как и большинство оболочек, поддерживает только целочисленную арифметику по модулю 2 64 (или по модулю 2 32 для более старых версий bash и некоторых других оболочек на 32-разрядных машинах).

Bash предлагает дополнительный удобный синтаксис, когда вы хотите выполнить присваивания или проверить, равно ли выражение 0, но не заботитесь о результате. Эта конструкция также существует в ksh и zsh, но не в простой sh.

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

В дополнение к целочисленной арифметике, exprпредлагает несколько функций для работы со строками. К ним также относятся функции оболочек POSIX, за исключением одного: expr STRING : REGEXPпроверяет, соответствует ли строка указанному регулярному выражению. Оболочка POSIX не может сделать это без внешних инструментов, но bash может с [[ STRING =~ REGEXP ]]другим синтаксисом регулярных выраженийexprэто классический инструмент и использует BRE, bash использует ERE).

Если вы не поддерживаете сценарии, которые работают на 20-летних системах, вам не нужно знать, что exprкогда-либо существовало. Используйте арифметику оболочки.

Жиль "ТАК - перестань быть злым"
источник
expr foo : '\(.\)'также делает извлечение текста. bash«S BASH_REMATCHдостигает что - то подобное. Он также выполняет сравнение строк, чего [не делает POSIX (хотя можно представить способы его использования sort).
Стефан Шазелас
подчеркивать как синтаксический заполнитель --- вы Schemer, @Giles? :]
RubyTuesdayDONO
1
@RubyTuesdayDONO Я не использовал здесь подчеркивание. Вы неправильно читаете U + 2026 ГОРИЗОНТАЛЬНЫЙ ЭЛЛИПСИС? Если это так, попробуйте использовать шрифт большего размера.
Жиль "ТАК - перестань быть злым"
@ Джайлс - хорошо, да, это выглядит только как подчеркивание из-за моего размера шрифта. для меня «Schemer» - это дополнение, и оно не похоже на то, что многоточие и подчеркивание меняют значение в любом случае… не нужно над ним
зацикливаться
12

Используйте скобки с кавычками:

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

Кавычки не позволяют bash интерпретировать скобки как синтаксис bash.

Николас Рауль
источник
2
Николас иллюстрирует, но не объясняет, что токены в exprкомандной строке должны быть разделены пробелами; так; например, expr 3 "*" "(2" "+" "1)" не будет работать . (Кроме того, кстати, вам, вероятно, не нужно цитировать +.)
G-Man говорит: «Восстановите Монику»
Скобки не ключевые слова , как whileи [[они синтаксис так. Если бы они были ключевыми словами, они не были бы интерпретированы как таковые в аргументах команды. Вам нужны кавычки, чтобы bash не анализировал их, а вместо этого видел строковый литерал.
Жиль "ТАК - перестань быть злым"
1

Если у вас есть до н.

echo '3 * (2 + 1)'|bc 
9                                                                    
грабить
источник