Bash shell中$ {var},"$ var"和"$ {var}"之间有什么区别?

She*_*rSt 114 variables syntax bash shell

标题是什么:将变量封装在{},""或者"{}" 是什么意思?"我无法在网上找到任何关于此的解释 - 除了使用符号之外我还没有能够引用它们不产生任何东西.

这是一个例子:

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")
Run Code Online (Sandbox Code Playgroud)

这个:

for group in "${groups[@]}"; do
    echo $group
done
Run Code Online (Sandbox Code Playgroud)

证明与此有很大不同:

for group in $groups; do
    echo $group
done
Run Code Online (Sandbox Code Playgroud)

还有这个:

for group in ${groups}; do
    echo $group
done
Run Code Online (Sandbox Code Playgroud)

只有第一个完成我想要的东西:遍历数组中的每个元素.我不是之间的差异是很清楚$groups,"$groups",${groups}"${groups}".如果有人能解释,我会很感激.

作为一个额外的问题 - 是否有人知道引用这些封装的可接受方式?

Thi*_*Not 200

大括号($var对比${var})

在大多数情况下,$var并且${var}是相同的:

var=foo
echo $var
# foo
echo ${var}
# foo
Run Code Online (Sandbox Code Playgroud)

仅需要大括号来解决表达式中的歧义:

var=foo
echo $varbar
# Prints nothing because there is no variable 'varbar'
echo ${var}bar
# foobar
Run Code Online (Sandbox Code Playgroud)

行情($varvs. "$var"vs. "${var}")

当您在变量周围添加双引号时,您告诉shell将其视为单个单词,即使它包含空格:

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
Run Code Online (Sandbox Code Playgroud)

将该行为与以下内容进行对比:

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
Run Code Online (Sandbox Code Playgroud)

$varvs.相比${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
Run Code Online (Sandbox Code Playgroud)

请注意,"${var}bar"在上面的第二个示例中也可以编写"${var}"bar,在这种情况下,您不再需要大括号,即"$var"bar.但是,如果您的字符串中有很多引号,则这些替代形式可能难以阅读(因此难以维护).这个页面提供了对Bash中引用的一个很好的介绍.

数组($varvs. $var[@]vs. ${var[@]})

现在为您的阵列.根据bash手册:

引用不带下标的数组变量等效于引用下标为0的数组.

换句话说,如果你不提供索引[],你得到数组的第一个元素:

foo=(a b c)
echo $foo
# a
Run Code Online (Sandbox Code Playgroud)

这完全一样

foo=(a b c)
echo ${foo}
# a
Run Code Online (Sandbox Code Playgroud)

要获取数组的所有元素,您需要使用@索引,例如${foo[@]}.数组需要大括号,因为没有它们,shell会先扩展$foo部分,给出数组的第一个元素后跟一个文字[@]:

foo=(a b c)
echo ${foo[@]}
# a b c
echo $foo[@]
# a[@]
Run Code Online (Sandbox Code Playgroud)

这个页面很好地介绍了Bash中的数组.

行情重访(${foo[@]}对比"${foo[@]}")

你没有问过这个,但这是一个微妙的差异,这是很好的了解.如果数组中的元素可以包含空格,则需要使用双引号,以便将每个元素视为单独的"单词:"

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
Run Code Online (Sandbox Code Playgroud)

将此与没有双引号的行为进行对比:

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
Run Code Online (Sandbox Code Playgroud)

  • @NamNguyen如果你想谈论其他形式的[参数扩展](http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html),至少还有十几个:`$ {parameter:-word}`,`$ {parameter:= word}`,`$ {parameter#word}`,`$ {parameter/pattern/string}`,依此类推.我认为这些超出了这个答案的范围. (4认同)
  • 还有另一种情况:`$ {var:?}`,当未设置或未设置变量时将提供错误.REF:https://github.com/koalaman/shellcheck/wiki/SC2154 (3认同)
  • 这不是一个很长的答案,它是一个完整的答案. (3认同)

Tod*_*obs 11

TL; DR

您提供的所有示例都是Bash Shell Expansions的变体.扩展以特定顺序发生,并且一些具有特定用例.

大括号作为令牌分隔符

${var}语法是主要用于限定暧昧令牌.例如,请考虑以下事项:

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo ${var1}2
foo2
Run Code Online (Sandbox Code Playgroud)

阵列扩展中的大括号

大括号需要访问数组的元素和其他特殊扩展.例如:

$ 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
Run Code Online (Sandbox Code Playgroud)

符号化

其余大多数问题都与引用以及shell如何标记输入有关.请考虑以下示例中shell执行分的方式不同:

$ 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
Run Code Online (Sandbox Code Playgroud)

@符号与引用不同于交互*.特别:

  1. $@ "[e] xpands到位置参数,从1开始.当扩展发生在双引号内时,每个参数都扩展为一个单独的单词."
  2. 在一个数组中,"[i]这个单词是双引号,${name[*]}扩展为一个单词,每个数组成员的值由IFS变量的第一个字符分隔,并将${name[@]}name的每个元素展开为一个单独的单词."

您可以按如下方式查看此操作:

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

$ count_params "$@"
3

$ count_params "$*"
1
Run Code Online (Sandbox Code Playgroud)

当变量引用带有空格或特殊字符的值可能会阻止shell按照您的意图进行分词时,引用扩展的使用非常重要.有关引用如何在Bash中工作的详细信息,请参阅引用.


Jon*_*ler 7

您需要区分数组和简单变量 - 您的示例是使用数组.

对于普通变量:

  • $var${var}完全相同.
  • "$var""${var}"完全相同.

但是,两对在所有情况下都不是100%相同.考虑下面的输出:

$ var="  abc  def  "
$ printf "X%sX\n" $var
XabcX
XdefX
$ printf "X%sX\n" "${var}"
X  abc  def  X
$
Run Code Online (Sandbox Code Playgroud)

如果没有变量周围的双引号,则会丢失内部间距,并将扩展视为printf命令的两个参数.使用变量周围的双引号,将保留内部间距,并将扩展视为printf命令的一个参数.

对于数组,规则既相似又不同.

  • 如果groups是数组,引用$groups${groups}等于引用${groups[0]},则为数组的第0个元素.
  • 引用"${groups[@]}"类似于引用"$@"; 它保留数组的各个元素中的间距,并返回值列表,每个元素的元素一个值.
  • 引用${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
$
Run Code Online (Sandbox Code Playgroud)

使用*而不是@导致微妙的不同结果.

另请参见如何迭代bash脚本中的参数.