Парентез в арифметиці expr: 3 * (2 + 1)


59

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

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

Як виразити пріоритет оператора в bash?

Відповіді:


40

Ще один спосіб використання letbash вбудований:

$ 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]. Чи є їх причиною уникати цього синтаксису?
Гордон

73

Ви можете використовувати арифметичне розширення замість цього.

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

На мою особисту думку, це виглядає трохи приємніше, ніж використання expr.

З man bash

Арифметичне розширення Арифметичне розширення дозволяє оцінити арифметичний вираз і замінити результат. Формат для арифметичного розширення:

         $((expression))

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

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


1
Крім читабельності, це також не вимагає форсування додаткового процесу для виконання арифметики; вона обробляється самою оболонкою.
чепнер

Зауважте, що в оболонках POSIX воно підлягає розщепленню слів, тому цитувати його в контексті списку є доброю звичкою.
Стефан Шазелас

Коли я спробую це в оболонці bash, я отримую "Незаконне ім'я змінної".
lordhog

40

Немає причин використовувати exprдля арифметики в сучасних оболонках.

POSIX визначає $((...))оператора розширення. Таким чином, ви можете використовувати це у всіх сумісних оболонках POSIX (у shвсіх сучасних Unix-like, 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 ]]іншим синтаксисом regexpexprкласичний інструмент і використовує BRE, bash використовує ERE).

Якщо ви не підтримуєте сценарії, які працюють у 20-річних системах, вам не потрібно знати, що exprколи-небудь існували. Використовуйте арифметику оболонки.


expr foo : '\(.\)'також робить вилучення тексту. bashРосія BASH_REMATCHдосягає чогось подібного. Він також робить рядкове порівняння, що POSIX [не робить (хоча можна уявити способи використання sortдля цього).
Стефан Шазелас

підкреслити як заповнювач синтаксису --- ти Schemer, @Giles? :]
RubyTuesdayDONO

1
@RubyTuesdayDONO Я тут не використовував підкреслення. Ви неправильно читаєте U + 2026 HORIZONTAL ELLIPSIS? Якщо так, спробуйте скористатися більшим шрифтом.
Жиль

@Giles - добре, так, це виглядає лише як підкреслення через мій шрифт. для мене "Schemer" - це доповнення, і це не так, як еліпсис проти підкреслення все одно змінює значення ... немає необхідності перебирати це: /
RubyTuesdayDONO

12

Використовуйте дужки з лапками:

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

Цитати заважають bash інтерпретувати дужки як синтаксис bash.


2
Те, що Ніколас ілюструє, але не пояснює, це те, що маркери в exprкомандному рядку повинні бути розділені пробілами; тому; наприклад, expr 3 "*" "(2" "+" "1)" не буде працювати . (Крім того, BTW, вам, мабуть, не потрібно цитувати +.)
G-Man

Дужки не є ключовими словами , як whileі [[, вони синтаксис. Якби вони були ключовими словами, вони не були б інтерпретовані як такі в аргументах команд. Вам потрібні лапки, щоб bash не розбирав їх, а натомість бачив рядковий літерал.
Жиль

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.