Подтвердить что ты не робот

В чем разница между ${var}, "$ var" и "$ {var}" в оболочке Bash?

Что говорит название: что означает инкапсуляция переменной в {}, "" или "{} "? Я не смог найти никаких объяснений в Интернете по этому поводу - я не был могут ссылаться на них, за исключением использования символов, которые ничего не дают.

Вот пример:

declare -a groups

groups+=("CN=exampleexample,OU=exampleexample,OU=exampleexample,DC=example,DC=com")
groups+=("CN=example example,OU=example example,OU=example example,DC=example,DC=com")

Это:

for group in "${groups[@]}"; do
    echo $group
done

Доказано, что он сильно отличается от этого:

for group in $groups; do
    echo $group
done

и это:

for group in ${groups}; do
    echo $group
done

Только первый выполняет то, что я хочу: итерации по каждому элементу массива. Я не совсем понимаю различия между $groups, "$groups", ${groups} и "${groups}". Если бы кто-нибудь мог это объяснить, я был бы признателен.

В качестве дополнительного вопроса - кто-нибудь знает приемлемый способ ссылки на эти инкапсуляции?

4b9b3361

Ответ 1

Подтяжки ($var против ${var})

В большинстве случаев $var и ${var} совпадают:

var=foo
echo $var
# foo
echo ${var}
# foo

Скобки необходимы только для устранения двусмысленности в выражениях:

var=foo
echo $varbar
# Prints nothing because there is no variable 'varbar'
echo ${var}bar
# foobar

Цитаты ($var vs. "$var" vs. "${var}")

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

var="foo bar"
for i in "$var"; do # Expands to 'for i in "foo bar"; do...'
    echo $i         #   so only runs the loop once
done
# foo bar

Сравните это поведение со следующим:

var="foo bar"
for i in $var; do # Expands to 'for i in foo bar; do...'
    echo $i       #   so runs the loop twice, once for each argument
done
# foo
# bar

Как и в случае с $var vs. ${var}, привязки нужны только для устранения неоднозначности, например:

var="foo bar"
for i in "$varbar"; do # Expands to 'for i in ""; do...' since there is no
    echo $i            #   variable named 'varbar', so loop runs once and
done                   #   prints nothing (actually "")

var="foo bar"
for i in "${var}bar"; do # Expands to 'for i in "foo barbar"; do...'
    echo $i              #   so runs the loop once
done
# foo barbar

Обратите внимание, что "${var}bar" во втором примере выше также может быть записано "${var}"bar, и в этом случае вам больше не нужны фигурные скобки, т.е. "$var"bar. Однако, если у вас много котировок в вашей строке, эти альтернативные формы могут быть трудночитаемыми (и поэтому их трудно поддерживать). Эта страница дает хорошее представление о цитировании в Bash.

Массивы ($var против $var[@] против ${var[@]})

Теперь для вашего массива. В соответствии с bash руководство:

Ссылка на переменную массива без индекса эквивалентна привязке массива с индексом 0.

Другими словами, если вы не указали индекс с [], вы получите первый элемент массива:

foo=(a b c)
echo $foo
# a

Что точно такое же, как

foo=(a b c)
echo ${foo}
# a

Чтобы получить все элементы массива, вам нужно использовать @ в качестве индекса, например. ${foo[@]}. Скобки требуются с массивами, потому что без них оболочка сначала расширяет часть $foo, давая первый элемент массива, за которым следует буква [@]:

foo=(a b c)
echo ${foo[@]}
# a b c
echo $foo[@]
# a[@]

Эта страница является хорошим введением в массивы в Bash.

Цитаты пересмотрены (${foo[@]} vs. "${foo[@]}")

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

foo=("the first" "the second")
for i in "${foo[@]}"; do # Expands to 'for i in "the first" "the second"; do...'
    echo $i              #   so the loop runs twice
done
# the first
# the second

Контрастируйте это с поведением без двойных кавычек:

foo=("the first" "the second")
for i in ${foo[@]}; do # Expands to 'for i in the first the second; do...'
    echo $i            #   so the loop runs four times!
done
# the
# first
# the
# second

Ответ 2

TL; DR

Все приведенные примеры - это варианты Bash Расширения оболочки. Расширения происходят в определенном порядке, а некоторые имеют конкретные варианты использования.

Брекеты как разделители токенов

Синтаксис ${var} в основном используется для разграничения неоднозначных токенов. Например, рассмотрим следующее:

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo ${var1}2
foo2

Подтяжки в расширении массива

Скобки необходимы для доступа к элементам array и для других специальные расширения. Например:

$ foo=(1 2 3)

# Returns first element only.
$ echo $foo
1

# Returns all array elements.
$ echo ${foo[*]}
1 2 3

# Returns number of elements in array.
$ echo ${#foo[*]}
3

лексемизация

Большинство остальных вопросов связаны с цитированием и как оболочка токенизирует ввод. Рассмотрим разницу в том, как оболочка выполняет разбиение слов в следующих примерах:

$ var1=foo; var2=bar; count_params () { echo $#; }

# Variables are interpolated into a single string.
$ count_params "$var1 $var2"
1

# Each variable is quoted separately, created two arguments.
$ count_params "$var1" "$var2"
2

Символ @ взаимодействует с цитированием иначе, чем *. В частности:

  • [email protected] "[e] xpands к позиционным параметрам, начиная с 1. Когда расширение происходит в двойных кавычках, каждый параметр расширяется до отдельного слова."
  • В массиве "[i] f слово double-quoted, ${name[*]} расширяется до одного слова со значением каждого элемента массива, разделенного первым символом переменной IFS, а ${name[@]} расширяет каждый элемент имени для отдельного слова."

Вы можете увидеть это в действии следующим образом:

$ count_params () { echo $#; }
$ set -- foo bar baz 

$ count_params "[email protected]"
3

$ count_params "$*"
1

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

Ответ 3

Вам нужно различать массивы и простые переменные — и ваш пример использует массив.

Для простых переменных:

  • $var и ${var} в точности эквивалентны.
  • "$var" и "${var}" в точности эквивалентны.

Однако две пары не являются на 100% идентичными во всех случаях. Рассмотрим следующий результат:

$ var="  abc  def  "
$ printf "X%sX\n" $var
XabcX
XdefX
$ printf "X%sX\n" "${var}"
X  abc  def  X
$

Без двойных кавычек вокруг переменной внутреннее расстояние теряется, а расширение рассматривается как два аргумента для команды printf. При двойных кавычках вокруг переменной внутренний интервал сохраняется, и расширение рассматривается как один аргумент команды printf.

С массивами правила одинаковы и различны.

  • Если groups - массив, ссылка на $groups или ${groups} эквивалентна привязке ${groups[0]}, нулевого элемента массива.
  • Ссылка "${groups[@]}" аналогична привязке "[email protected]"; он сохраняет интервал в отдельных элементах массива и возвращает список значений, по одному значению для каждого элемента массива.
  • Ссылка на ${groups[@]} без двойных кавычек не сохраняет интервал и может вводить больше значений, чем есть элементы в массиве, если некоторые из элементов содержат пробелы.

Например:

$ groups=("abc def" "  pqr  xyz  ")
$ printf "X%sX\n" ${groups[@]}
XabcX
XdefX
XpqrX
XxyzX
$ printf "X%sX\n" "${groups[@]}"
Xabc defX
X  pqr  xyz  X
$ printf "X%sX\n" $groups
XabcX
XdefX
$ printf "X%sX\n" "$groups"
Xabc defX
$

Использование * вместо @ приводит к тонко различным результатам.

См. также Как перебрать аргументы в bash script.

Ответ 4

Второе предложение первого абзаца в разделе "Расширение параметра" в man bash говорит:

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

Что говорит вам, что имя - это просто фигурные скобки, и основная цель - уточнить, где начинается и заканчивается имя:

foo='bar'
echo "$foobar"
# nothing
echo "${foo}bar"
barbar

Если вы читаете далее, вы обнаружите,

Скобки необходимы, когда параметр является позиционным параметром с более чем одной цифрой...

Пусть тест:

$ set -- {0..100}
$ echo $22
12
$ echo ${22}
20

Да. Ухоженная. Я, честно говоря, не знал этого, прежде чем писать (раньше у меня никогда не было более 9 позиционных параметров.)

Конечно, вам также нужны скобки для создания мощных функций расширения параметров, таких как

${parameter:-word}
${parameter:=word}
${parameter:?word}
… [read the section for more]

а также расширение массива.

Ответ 5

Связанный случай, не описанный выше. Кажется, что для переменной test -n можно изменить значение пустой переменной. Это специально приведено в качестве примера в тексте info для coreutils, но на самом деле не объяснено:

16.3.4 String tests
-------------------

These options test string characteristics.  You may need to quote
STRING arguments for the shell.  For example:

     test -n "$V"

  The quotes here prevent the wrong arguments from being passed to
`test' if `$V' is empty or contains special characters.

Я хотел бы услышать подробное объяснение. Мое тестирование подтверждает это, и теперь я цитирую мои переменные для всех строковых тестов, чтобы избежать -z и -n вернуть тот же результат.

$ unset a
$ if [ -z $a ]; then echo unset; else echo set; fi
unset
$ if [ -n $a ]; then echo set; else echo unset; fi    
set                                                   # highly unexpected!

$ unset a
$ if [ -z "$a" ]; then echo unset; else echo set; fi
unset
$ if [ -n "$a" ]; then echo set; else echo unset; fi
unset                                                 # much better

Ответ 6

Ну, я знаю, что инкапсуляция переменной помогает вам работать с чем-то вроде:

${groups%example}

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

Теперь, если вы видите свой код, все волшебство находится внутри

${groups[@]}

волшебство там, потому что вы не можете писать просто: $groups[@]

Вы помещаете свою переменную внутри {}, потому что вы хотите использовать специальные символы [] и @. Вы не можете просто назвать или вызывать свою переменную: @ или something[], потому что это зарезервированные символы для других операций и имен.