Ссылки на переменные в сценарии оболочки

10

В сценарии оболочки, если я определяю переменную, такую ​​как FOO=25, есть ли разница между ссылками на нее как $FOO$и ${FOO}$?

Karnivaurus
источник
2
Вы не должны использовать $в конце концов! Он будет обрабатываться как обычный символ, а не как часть имени переменной.
Byte Commander

Ответы:

16

В большинстве ситуаций оба $varи ${var}одинаковы. (Обратите внимание, что вы не должны использовать $в конце!)

Один пример, где вам нужны фигурные скобки, - это когда вам нужно поместить переменную в непрерывную строку.

Пример:

var=hel
echo ${var}lo

будет выводить hello.

Айзуддин Зали
источник
11

Чтобы ответить на ваш главный вопрос, ${foo}это называется «расширение параметров» . Чтобы быть точным, $сам запускает расширение параметров, { }они на самом деле необязательны в соответствии со спецификацией POSIX , но могут быть полезны для указания конца имени переменной:

$ foo="bar"
$ echo $fooBaz   ## fails, no variable named $fooBaz exists

$ echo ${foo}Baz ## works, $foo is expanded and the string Baz is appended
barBaz

В основном $fooи ${foo}идентичны. За исключением случаев, подобных описанному выше, или когда вы выполняете манипуляции со строками , они полностью эквивалентны.

Тем не менее, вы не должны использовать ни один из них. Эмпирическое правило заключается в том, что, за очень немногими исключениями, вы должны всегда использовать "$foo"или "${foo}"и никогда $fooили ${foo}. Вы всегда должны заключать в кавычки свои переменные, чтобы избежать вызова оператора split + glob (подробнее об этом позже). Вы, конечно, не хотите $foo$. Финал не $имеет значения:

$ foo="bar"
$ echo "$foo$"
bar$

Итак, в то время как переменные без кавычек иногда в порядке:

$ echo $foo
bar

Их обычно нет, и их действительно следует избегать:

$ if [ -n $foo ]; then echo empty; else echo "not empty"; fi ## fails
empty

$ if [ -n "$foo" ]; then echo empty; else echo "not empty"; fi ## works
not empty

Обратите внимание, что скобки здесь тоже не помогают:

$ if [ -n ${foo} ]; then echo empty; else echo "not empty"; fi  ## fails
empty

$ if [ -n "${foo}" ]; then echo empty; else echo "not empty"; fi ## works
not empty

Когда вы используете $fooили ${foo}, оболочка разделяет значение, сохраненное в переменной на пустое пространство (это можно изменить, установив IFSпеременную в другое значение), в список, а затем каждый элемент списка обрабатывается как глобальный шаблон и разворачивается в любые соответствующие файлы или каталоги. Это известно как оператор split + glob . Для иллюстрации рассмотрим каталог с двумя файлами:

$ ls -l
-rw-r--r-- 1 terdon terdon 0 Oct  9 18:16 file1
-rw-r--r-- 1 terdon terdon 0 Oct  9 18:16 file2

Теперь давайте установим переменную в foo *:

$ foo="foo *"

Что произойдет, если мы попытаемся проверить, существует ли файл с таким именем?

$ if [ -e $foo ]; then echo "file exists"; else echo "no such file"; fi
file exists

Переменная была разбита на fooи *и, поскольку *это подстановочный знак, который соответствует любой строке, оболочка сообщает вам, что файл называется foo *exxists. Однако, если мы процитируем это правильно, этого не произойдет:

$ if [ -e "$foo" ]; then echo "file exists"; else echo "no such file"; fi
no such file

Это был тривиальный пример, чтобы проиллюстрировать это. Представь, если бы я использовал rmвместо этого, echoхотя.

Итак, первое правило: всегда указывайте свои переменные. Вы можете использовать либо "$foo"или, "${foo}"но процитировать его в любом случае. Чтобы узнать больше о безопасном использовании переменных, взгляните на эти сообщения:

terdon
источник
Я не хотел редактировать ваш отличный ответ, но не должен $ var="foo *"ли $ foo="foo *"? Вы не используете varнигде.
Джо
@ Джо, о боже! Да, конечно, это должно быть, спасибо за указание на это. Не стесняйтесь исправлять такие ошибки, предлагая редактирование, кстати. Подобные вещи очень поощряются, чтобы избежать глупых ошибок, подобных этой.
тердон