Когда нам нужны фигурные скобки вокруг переменных оболочки?

659

В сценариях оболочки, когда мы используем {}при расширении переменных?

Например, я видел следующее:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

Есть ли существенная разница или это просто стиль? Один предпочтительнее другого?

Новый пользователь
источник

Ответы:

754

В этом конкретном примере это не имеет значения. Тем не менее, {}в ${}полезно, если вы хотите раскрыть переменную fooв строке

"${foo}bar"

так "$foobar"как вместо этого расширит переменную, обозначенную foobar.

Фигурные скобки также безусловно требуются, когда:

  • расширение элементов массива, как в ${array[42]}
  • используя операции расширения параметров, как в ${filename%.*}(удалить расширение)
  • расширение позиционных параметров за пределы 9: "$8 $9 ${10} ${11}"

Делать это везде, а не только в потенциально неоднозначных случаях, можно считать хорошей практикой программирования. Это делается как для согласованности, так и для избежания неожиданностей, например $foo_$bar.jpg, когда визуально не очевидно, что подчеркивание становится частью имени переменной.

Fred Foo
источник
106
{}известен как расширение скобки . ${}известен как расширение переменной. Они делают разные вещи. Я бы поддержал вас, за исключением бита без расширения.
Спенсер Рэтбун
5
@NewUser " Так что кроме массивов это на самом деле не требуется " Не так, фигурные скобки необходимы для PARAMETER EXPANSION , очень полезной конструкции в сценариях. Я видел много сценариев sed и awk, которые можно заменить небольшим расширением параметров.
SiegeX
10
@caffinatedmonkey $()используется для выполнения команды, которая md5sum=$(md5sum foo.bin)будет хранить выходные данные md5sum foo.binв переменной md5sum, теперь доступной через ${md5sum}. Кроме того, +1 и еще много в духе OP за упоминание, что это хорошая практика, чтобы быть явным!
L0j1k
11
@ L0j1k Говоря о явности, я считаю важным упомянуть, что $()выполняет свою команду из подоболочки .
Адриан Гюнтер,
2
@karatedog ${1:-20}- это форма расширения параметров. Здесь это неочевидно, потому что в основном используются цифры и арифметические операторы, которые обманывают нас, думая, что здесь задействована арифметика, но на самом деле это относится к позиционному параметру $1, который, если не определен, будет заменен значением по умолчанию 20(синтаксис равен ${variable:-default_value}).
Аарон
127

Переменные объявляются и присваиваются без $и без {}. Вы должны использовать

var=10

назначить. Чтобы читать из переменной (другими словами, «расширить» переменную), вы должны использовать $.

$var      # use the variable
${var}    # same as above
${var}bar # expand var, and append "bar" too
$varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.

Это иногда смущало меня - в других языках мы обращаемся к переменной одинаково, независимо от того, находится она слева или справа от присваивания. Но shell-скриптинг отличается, $var=10не делает то, что вы думаете, что делает!

Аарон МакДейд
источник
34

Вы используете {}для группировки. Скобки необходимы для разыменования элементов массива. Пример:

dir=(*)           # store the contents of the directory into an array
echo "${dir[0]}"  # get the first entry.
echo "$dir[0]"    # incorrect
Гленн Джекман
источник
Я не мог понять первую строку dir=(*). Насколько я знаю, dirэто встроенная команда для отображения содержимого каталога (эквивалентно ls -C -b). Не могли бы вы объяснить?
Джарвис
1
В программировании оболочки команды и аргументы должны быть отделены друг от друга пробелом. Здесь вы видите знак равенства без пробелов, это означает, что это присваивание переменной. dirэто имя переменной, а круглые скобки используются для сбора расширения имени файла *в массив.
Гленн Джекман
27

Вы также можете сделать некоторые манипуляции с текстом внутри фигурных скобок:

STRING="./folder/subfolder/file.txt"
echo ${STRING} ${STRING%/*/*}

Результат:

./folder/subfolder/file.txt ./folder

или

STRING="This is a string"
echo ${STRING// /_}

Результат:

This_is_a_string

Вы правы, в «обычных переменных» они не нужны ... Но это более полезно для отладки и чтения сценария.

SierraX
источник
11

Конец имени переменной обычно обозначается пробелом или новой строкой. Но что, если мы не хотим пробела или новой строки после печати значения переменной? Фигурные скобки указывают интерпретатору оболочки, где находится конец имени переменной.

Классический пример 1) - переменная оболочки без конечных пробелов

TIME=10

# WRONG: no such variable called 'TIMEsecs'
echo "Time taken = $TIMEsecs"

# What we want is $TIME followed by "secs" with no whitespace between the two.
echo "Time taken = ${TIME}secs"

Пример 2) Java classpath с версионными банками

# WRONG - no such variable LATESTVERSION_src
CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar

# RIGHT
CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar

(Ответ Фреда уже говорит об этом, но его пример слишком абстрактен)

Шридхар Сарнобат
источник
5

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

Хорошо быть не слишком осторожным и использовать его {}для расширения переменных оболочки, даже если нет места для двусмысленности.

Например:

dir=log
prog=foo
path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
path_copy=${path}             # {} is totally unnecessary
archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name

Итак, лучше написать три строки:

path=/var/$dir/$prog
logfile=$path/$prog.log
path_copy=$path

что определенно более читабельно.

Поскольку имя переменной не может начинаться с цифры, оболочка не нуждается {}в пронумерованных переменных (например $1, $2и т. Д.), Если за таким расширением не следует цифра. Это слишком тонко, и это явно позволяет использовать {}в таких контекстах:

set app      # set $1 to app
fruit=$1le   # sets fruit to apple, but confusing
fruit=${1}le # sets fruit to apple, makes the intention clear

Видеть:

codeforester
источник
1
It's good to be not over-cautiousИнтересно, что думает большинство людей. Всегда используйте фигурные скобки, чтобы не забыть их, когда они нужны, или используйте их только там, где это необходимо, для улучшения читабельности.
Роджер Даль
1
Я думаю, что из-за недостатка осведомленности программисты используют керли, даже когда они не нужны. Это невежество похоже на другую распространенную ошибку - не использовать двойные кавычки для предотвращения непреднамеренного расщепления или смещения слов. По сути, реальность такова, что программисты не так серьезно относятся к сценариям оболочки, как другие языки сценариев, такие как Python и Ruby.
Codeforester
1
Правда что. Моя любимая мозоль - то, что все, кажется, думают, что все переменные должны быть заглавными буквами в сценариях оболочки :)
Роджер Даль
2

Следуя предложению SierraX и Питера о манипулировании текстом, фигурные скобки {}используются, например, для передачи переменной команде:

Допустим, у вас есть файл sposi.txt, содержащий первую строку известного итальянского романа:

> sposi="somewhere/myfolder/sposi.txt"
> cat $sposi

Ouput: quel ramo del lago di como che volge a mezzogiorno

Теперь создайте две переменные:

# Search the 2nd word found in the file that "sposi" variable points to
> word=$(cat $sposi | cut -d " " -f 2)

# This variable will replace the word
> new_word="filone"

Теперь замените содержимое переменной word на new_word внутри файла sposi.txt.

> sed -i "s/${word}/${new_word}/g" $sposi
> cat $sposi

Ouput: quel filone del lago di como che volge a mezzogiorno

Слово "ramo" было заменено.

Фабио Наталини
источник
1
Это работает так же хорошо, без фигурных скобок вокруг переменных.
Armali
Вы можете исправить weel-known novelбит. Поддержал все же.
GSL