Круглая скобка в выраженной арифметике: 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 .


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 не анализировал их, а вместо этого видел строковый литерал.
— Жиль "ТАК - перестань быть злым"

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.