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

时间:2013-08-08 20:25:19

标签: bash shell variables syntax

标题是什么:将变量封装在{}"""{}中是什么意思?我无法在网上找到任何关于此的解释 - 除了使用符号之外,我无法引用它们,这些符号不会产生任何效果。

以下是一个例子:

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}"之间的差异并不十分清楚。如果有人能解释,我会很感激。

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

6 个答案:

答案 0 :(得分:179)

大括号($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"$var"对比"${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

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

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${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。但是,如果您的字符串中有很多引号,则这些替代形式可能难以阅读(因此难以维护)。 This page为Bash中的引用提供了很好的介绍。

数组($var$var[@]对比${var[@]}

现在为您的阵列。根据{{​​3}}:

  

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

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

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

完全相同
foo=(a b c)
echo ${foo}
# a

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

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

bash manual是对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

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

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

答案 1 :(得分:10)

TL; DR

您提供的所有示例都是Bash Shell Expansions的变体。扩展按特定顺序发生,有些具有特定用例。

作为令牌分隔符的大括号

${var}语法主要用于分隔模糊标记。例如,请考虑以下事项:

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

阵列扩展中的大括号

要求大括号访问array和其他special expansions的元素。例如:

$ 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

标记化

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

$ 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

@ 符号与引用的交互方式与 * 不同。具体做法是:

  1. $@“[e] xpands到位置参数,从1开始。当扩展发生在双引号内时,每个参数都会扩展为一个单独的单词。”
  2. 在一个数组中,“[i]这个单词是双引号,${name[*]}扩展为单个单词,每个数组成员的值用IFS变量的第一个字符分隔,{{1将name的每个元素扩展为一个单独的单词。“
  3. 您可以按如下方式查看此操作:

    ${name[@]}

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

答案 2 :(得分: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
$

如果变量周围没有双引号,则会丢失内部间距,并将扩展视为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
$

使用*代替@会导致微妙的结果。

另见How to iterate over the arguments in a bash script

答案 3 :(得分:3)

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]

以及阵列扩展。

答案 4 :(得分:3)

以上未涉及的相关案例。引用空变量似乎改变了test -n的内容。这是info的{​​{1}}文本中的一个示例,但没有真正解释过:

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

答案 5 :(得分:2)

嗯,我知道变量的封装可以帮助你处理类似的事情:

${groups%example}

或类似的语法,您希望在返回值之前对变量执行某些操作。

现在,如果你看到你的代码,那么所有的魔力都在

之内
${groups[@]}

魔法在那里因为你不能只写:$groups[@]

您将变量放在{}内,因为您想使用特殊字符[]@。您不能仅仅命名或调用变量:@something[],因为这些是其他操作和名称的保留字符。