$ VAR vs $ {VAR} и цитировать или не цитировать


139

я могу написать

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"

конечный результат для меня все кажется примерно одинаковым. Почему я должен написать один или другой? какие-нибудь из них не переносимы / POSIX?

Ответы:


99

VAR=$VAR1это упрощенная версия VAR=${VAR1}. Второе может сделать то, что первое не может, например, сослаться на индекс массива (не переносимый) или удалить подстроку (переносимый POSIX). См. Раздел « Дополнительная информация о переменных » руководства Bash для начинающих и расширения параметров в спецификации POSIX.

Использование кавычек вокруг переменной, как в rm -- "$VAR1"или rm -- "${VAR}"является хорошей идеей. Это делает содержимое переменной атомарной единицей. Если значение переменной содержит пробелы (ну, символы в $IFSспециальной переменной, пробелы по умолчанию) или символы глобинга, и вы не заключаете его в кавычки, то каждое слово рассматривается для генерации имени файла (глобирования), расширение которого дает столько аргументов, сколько вы делаешь.

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename

По переносимости: в соответствии с разделом 2.6.2 POSIX.1-2008 , фигурные скобки являются необязательными.


@shawn обновил мой вопрос, потому что мне также интересно узнать о переносимости
xenoterracide

@shawn: я сомневаюсь, что твой пример верен. У вас есть реальный пример оболочки, где var1=$varрасширение дает ошибку?
Алекс

@alex: Спасибо. Я думал, что проверил это в командной строке, но я сделал это неправильно. Я изменил пример.
Шон Дж. Гофф

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

9
@Shawn: в кавычках нет необходимости. Они необходимы в большинстве других применений, в том числе export VAR=$VAR1. Что касается фигурных скобок, они являются необязательными (см. Четвертый абзац цитируемого вами раздела; это относится ко всем оболочкам до POSIX и POSIX).
Жиль

61

${VAR}и $VARв точности эквивалентны. Для простого расширения переменной единственная причина использования ${VAR}- это когда синтаксический анализ в противном случае захватил бы слишком много символов в имя переменной, как в ${VAR1}_$VAR2(что без скобок было бы эквивалентно ${VAR1_}$VAR2). Наиболее украшенное разложение ( ${VAR:=default}, ${VAR#prefix}, ...) требуют брекетов.

В присваивании переменной разделение полей (то есть разделение в пустом пространстве в значении) и расширение имени пути (то есть глобализация) отключены, что VAR=$VAR1в точности эквивалентно VAR="$VAR1"во всех оболочках POSIX и во всех pre-POSIX sh, о которых я слышал , (Ссылка POSIX: простые команды ). По той же причине VAR=*надежно устанавливает VARбуквальную строку *; конечно VAR=a bустанавливает VARдля aпоскольку bотдельное слово в первую очередь. Вообще говоря, двойные кавычки не нужны, когда синтаксис оболочки ожидает одно слово, например, вcase … in (но не в шаблоне), но даже там вам нужно быть осторожным: например, POSIX указывает, чтоцели перенаправления ( >$filename) не требуют цитирования в сценариях, но некоторые оболочки, включая bash, требуют двойных кавычек даже в сценариях. См. Когда необходимо двойное цитирование? для более тщательного анализа.

В других случаях вам нужны двойные кавычки, в частности, export VAR="${VAR1}"(которые можно эквивалентно записать export "VAR=${VAR1}") во многих оболочках (POSIX оставляет этот случай открытым). Сходство этого случая с простыми назначениями и рассеянный характер списка случаев, когда вам не нужны двойные кавычки, - вот почему я рекомендую просто использовать двойные кавычки, если вы не хотите разбивать и использовать глобальные символы.


2
Как правило, я всегда буду цитировать расширения переменных, даже когда я знаю, что значение не будет содержать IFSсимволов, потому что я хочу быть в привычке. Единственным исключением является то, что я не цитирую значение при выполнении присваивания переменной (если не требуется, например, когда значение содержит пробел). Это делает подсветку синтаксиса редактора более полезной, когда есть подстановки команд, такие как FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}"). Вместо того, чтобы раскрашивать все в «строковый» цвет, я получаю подсветку синтаксиса вложенного кода. Это также, почему я избегаю кавычек.
Ричард Хансен

Хотя >$fileэто нормально в сценариях POSIX, он не находится в bash, даже когда неинтерактивен (если только соблюдение POSIX не выполняется с помощью $POSIXLY_CORRECTили --posix...).
Стефан Шазелас

Хотя это правда, что кавычки не нужны VAR=$VAR1, меня иногда удивляет то local VAR=$VAR1, что я помню, как по-другому работал, по крайней мере, в некоторых оболочках. Но я не могу воспроизвести расхождение.
dubiousjim

Хорошо, нашел проблему, которую я запомнил . Это проявляется только в некоторых оболочках.
dubiousjim

@dubiousjim local VAR=$VAR1это как export VAR=$VAR1, это зависит от оболочки.
Жиль

8

Цитата

Учтите, что двойные кавычки используются для раскрытия переменных, а одинарные кавычки используются для сильных кавычек, то есть без расширения.

Расширение:

this='foo'
that='bar'
these="$this"
those='$that'

Выход:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that

Возможно, стоит упомянуть, что вы должны использовать цитату везде, где это возможно, по нескольким причинам, среди которых лучшими являются то, что это считается наилучшей практикой, и для удобства чтения. Кроме того, потому что Bash иногда бывает причудливым и часто из-за кажущихся нелогичными или неразумными / неожиданными способами, а цитата меняет неявные ожидания на явные, что уменьшает эту поверхность ошибки (или потенциальную вероятность появления в ней).

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

подмена

Теперь рассмотрим также, что конструкция "${somevar}"используется для операций замещения. Несколько вариантов использования, таких как замена и массивы.

Замена (зачистка):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar

Замена (замена):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful

Массивы:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt

Все это едва царапает поверхность конструкции "${var}"замещения. Оптимальной ссылкой для сценариев оболочки Bash является онлайновая ссылка libre, TLDP Проект документации Linuxhttps://www.tldp.org/LDP/abs/html/parameter-substitution.html


1
очень информативно.
Орион Элензил

0
ls -la

lrwxrwxrwx.  1 root root      31 Nov 17 13:13 prodhostname
lrwxrwxrwx.  1 root root      33 Nov 17 13:13 testhostname
lrwxrwxrwx.  1 root root      32 Nov 17 13:13 justname

конец тогда:

env=$1
    if [ ! -f /dirname/${env}hostname ]

стоит упомянуть в качестве более четкого примера использования фигурных скобок

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.