Все ли эти инструкции bash эквивалентны?

#bash

#bash

Вопрос:

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

Этот код:

 var=$RANDOM

var1=$[ $var % 13 ]
echo "var1 = $var1"

var2=$( $var % 13 )
echo "var2 = $var2"

var3=$(( $var % 13 ))
echo "var3 = $var3"

var4=`expr $var % 13` # Note revised from original post by adding the expr
echo "var4 = $var4"
  

Выдает этот вывод:

 var1 = 7
./question: line 7: 23225: command not found
var2 = 
var3 = 7
var4 = 7
  

Таким образом, не работает только инструкция var2. Мой вопрос таков: это только вопрос личного выбора относительно того, какой из трех других следует использовать, или есть другие соображения, которые следует принимать во внимание?

(Я тщательно отредактировал этот вопрос после просмотра ответов. Надеюсь, я делаю это правильно.)

Комментарии:

1. Я запустил их сейчас и отредактировал исходный вопрос с результатом.

Ответ №1:

Версии $[] и $(()) по существу взаимозаменяемы, за исключением того, что не все оболочки поддерживают $[] , поэтому не используйте это — используйте $(()) вместо этого. Кроме того, в обеих этих формах переменные, используемые в выражении, будут автоматически расширены, поэтому вам не нужно использовать $ внутри них:

 var=$RANDOM
echo $var             # prints 7482 (in my example run)
echo $[ $var % 13 ]   # prints 7
echo $[ var % 13 ]    # prints 7
echo $(( $var % 13 )) # prints 7
echo $(( var % 13 ))  # prints 7
  

expr на самом деле это команда, которая выполняет вычисление выражения — она обладает большинством тех же возможностей, что и встроенное вычисление выражения, но без некоторых тонкостей. Например, вы должны использовать $ для расширения переменных и должны избегать любых операторов, которые оболочка будет обрабатывать как специальные символы (например, < , > , | и т.д.):

 expr $var % 13        # prints 7
echo `expr $var % 13` # prints 7, just by a less direct route
  

$() полностью отличается — он не выполняет арифметическое вычисление, он запускает свое содержимое как команду. Фактически, это почти то же самое, что обратные кавычки (за исключением того, что их легче читать и у них гораздо более чистый синтаксис, поэтому я всегда использую их вместо обратных кавычек):

 echo $(expr $var % 13) # prints 7, same as with backquotes
  

И просто чтобы сделать это более сложным, позвольте мне добавить еще несколько опций:

 (( var5 = var % 13 )) # This is an arithmetic statement (that happens to contain an assignment)
echo $var5            # prints 7
let "var6 = var % 13" # Another form for pretty much the same thing
echo $var6            # prints 7

declare -i var7  # declare var7 as an integer...
var7="var % 13"  #  to force values assigned to it to be evaluated
echo $var7       # prints 7
  

Итак, что вы должны использовать? Я бы выбрал var3=$(( var % 13 )) для максимальной совместимости и (ИМХО) чистого синтаксиса.

Комментарии:

1. Отличный ответ, Гордон. Настолько полно и даже расширяет вопрос другими вариантами. С этого момента я буду использовать var3 = $ (( var % 13 )). Теперь я пойду, чтобы выяснить, как наградить вас за ваше замечание.

2. Я думаю, что единственный способ начислить баллы — проголосовать за это. Я пытался, но у меня недостаточно репутации, чтобы голосовать! У меня есть только 11, а вам нужно 15.

3. @grok: Я рад, что это было полезно. Поскольку вы задали исходный вопрос, вы должны быть в состоянии принять ответ (щелкните по контуру флажка слева от ответа), даже если вы еще не можете проголосовать.

4. Я нажал зеленую галочку, и она переместила ваш пост наверх, но он по-прежнему не позволяет мне проголосовать за вас из-за отсутствия репутации 15.

5. Я, наконец, набрал некоторую репутацию и смог поставить вам точку. Ура!

Ответ №2:

$[…] и $((…)) в точности эквивалентны. Однако $[…] форма является устаревшим расширением bash и zsh; $((…)) форма является стандартной.

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

$( $var % 13 ) просто сломан, он пытается вызвать $var (или, точнее, первое его слово) в качестве имени команды.

Комментарии:

1. Да, я не должен был включать $ ( $ var % 13 ). Я буду использовать $ ((…)) из-за того, что это стандартно.

Ответ №3:

Недопустимая команда

 var=$( $var % 13 )
  

Допустимая команда, но предпочитаете $(( var % 13 ))

 var=$(( $var % 13 ))
  

Недопустимая команда

 var=$var % 13
  

Результат: Таким образом, из всех только $(( var % 13 )) является допустимым способом получить значение по модулю $var на 13.

Обновить

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

Комментарии:

1. Извините, все. Я перепутал свой первый вопрос. Я собираюсь попробовать каждый из них сам, как я должен был сделать перед публикацией. Затем я вернусь и выясню, как опубликовать код.

2. После ваших правок я бы посоветовал использовать либо var1 , либо var3 способ вычисления по модулю. expr это внешняя программа, и для выполнения той же работы лучше использовать внутреннюю утилиту bash.

3. Раньше я делал много sh-скриптов, но это было 19 лет назад, поэтому я не в курсе новых дополнений. Я думаю, что 19 лет назад был доступен только способ expr, но сейчас я перестану его использовать.

Ответ №4:

 ((var%=13))
  

есть другой способ, ярлык для var=((var))

Комментарии:

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

2. Однако, когда я пытаюсь (( var3% = 13 )); echo «var3 = $ var3», я всегда получаю ноль. Возможно, я что-то недопонимаю.

3. var3=99; (( var3%=13 )); echo $var3 эхо 8 для меня в bash. GNU bash, Version 4.1.5(1)-release (i486-pc-linux-gnu)

4. Может быть, вы никогда не инициализировали var3?

5. Блестящее наблюдение! Я не инициализировал var3. Теперь я использую: var3=var; (( var3% = 13 )); echo «var3 = $ var3» и это работает. Извините, что я сомневался в вас.