Разница между let, expr и $ []

23

Я хочу знать, в чем именно разница между

a=$[1+1]
a=$((1+1))
let a=1+1
a=$(expr 1 + 1 )

Все 4 присваивают переменную a с 2, но в чем разница?

Из того, что я обнаружил до сих пор, является то, что expr медленнее, потому что это не настоящая встроенная оболочка. Но не более того.

ADDB
источник

Ответы:

25

Все они имеют дело с арифметикой, но по-разному, и переменная создается с помощью различных средств. Некоторые из них относятся только к bashоболочкам, а другие нет.

  • $((...))называется арифметическое расширение , которое является типичным bashи kshоболочек. Это позволяет делать простую целочисленную арифметику, но без всякой вещи с плавающей точкой. Результат выражения заменяет выражение, как echo $((1+1))станетecho 2
  • ((...))называется арифметической оценкой и может использоваться как часть if ((...)); thenили while ((...)) ; doутверждения. Расширение арифметического расширения $((..))заменяет вывод операции и может использоваться для назначения переменных, как в, i=$((i+1))но не может использоваться в условных выражениях.
  • $[...] старый синтаксис для арифметического расширения, который устарел. Смотрите также . Скорее всего, это сохранилось, чтобы старые bashсценарии не ломались. Это не сработало ksh93, поэтому я предполагаю, что этот синтаксис специфичен для bash. ПРИМЕЧАНИЕ : пробелы здесь очень важны; не путайте $[1+1]с такими вещами, как [ $a -eq $b ]. С [пробелами называется testкоманда, и вы обычно видите это в части принятия решений. Это очень отличается по поведению и цели.
  • letявляется ключевым словом bashand, kshкоторое позволяет создавать переменные с простой арифметической оценкой. Если вы попытаетесь назначить строку там, как let a="hello world"вы получите синтаксическую ошибку. Работает в bashа ksh93.
  • $(...)является подстановкой команды, где вы буквально берете выходные данные команды и присваиваете переменную. Ваша команда здесь expr, которая принимает позиционные аргументы, например expr arg1 arg2 arg3, пробелы важны. Это что-то вроде небольшого калькулятора командной строки для целочисленной арифметики, плюс некоторые вещи типа true / false и regex. Это нейтральная для оболочки команда.

Стоит также отметить, что арифметическое расширение и подстановка команд определены стандартом POSIX , а так letи $[...]нет.

Сергей Колодяжный
источник
1
((...))фактически может использоваться для заданий bash, kshа zsh: n=10; ((n+=10)); echo $nпечатает 20 и ((x=1)); echo $xпечатает 1.
Алексей Магура
1
@Alexej Спасибо. Ответ обновлен, эта часть удалена
Сергей Колодяжный
11
  • letКоманда выполняет арифметическую оценку и является встроенной в оболочку.

    • Запустите эту команду, и вы ничего не получите (только оценки):

      let 1+2
  • $(( ))используется для выполнения арифметического расширения : читайте здесь

    • Запустите этот, и вы получите ошибку (из-за расширения):

      $((1+2))
  • $[ ] старый синтаксис для арифметического расширения:

    Старый формат $ [expression] устарел и будет удален в следующей версии bash. Bash Man Page

  • expr это бинарная команда, если вы хотите сделать арифметическое раскрытие в подстановке команд, вы можете использовать ее:

    echo $(expr 1 + 2) 
    echo `expr 1 + 2`
Ravexina
источник
4

Поскольку в некоторых ответах выше конкретно упоминается ksh93, стоит отметить, что он может выполнять математические операции с плавающей запятой, например:

$ print $((1.0/3)) 
0.333333333333333333

Вы можете контролировать точность вывода с помощью printf, например:

$ printf "%.4f\n" $((1.0/3))
0.3333

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

$ print $((1/3))  
0

Это может быть полезно, когда вам нужна математика с плавающей точкой в ​​сценарии оболочки, поскольку вы можете избежать вызова внешней команды.

Sneezy
источник
Мне приходит в голову, что вам может быть интересно опубликовать ответ на вопрос: Могу ли я использовать дроби / десятичные дроби в скрипте bash? Несмотря на название, я думаю , что контекст ясно , что другие оболочки Bourne стиля актуальны, поэтому я отправил в zshответ . Поскольку kshи bashони больше похожи друг на друга (в целом), чем на что-либо zsh, мне кажется, что kh93основанный на этом вопрос ответ может быть весьма полезным.
Элия ​​Каган
1

letне работает в cron. Я думаю, что это связано с letсобственной средой. Используйте, $((...))так как это POSIX. Например,

let x=1+2
echo x=$x

в cron приводит к «x =», но «x = 3» при запуске из оболочки.

x=$((1+2))
echo x=$x

приводит к "х = 3" во всех случаях.

SparkEV
источник