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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18135451/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 00:02:07  来源:igfitidea点击:

What is the difference between ${var}, "$var", and "${var}" in the Bash shell?

bashshellvariablessyntax

提问by SheerSt

What the title says: what does it mean to encapsulate a variable in {}, "", or "{}"? I haven't been able to find any explanations online about this - I haven't been able to refer to them except for using the symbols, which doesn't yield anything.

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

Here's an example:

下面是一个例子:

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")

This:

这个:

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

Proves to be much different than this:

事实证明与此大不相同:

for group in $groups; do
    echo $group
done

and this:

和这个:

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

Only the first one accomplishes what I want: to iterate through each element in the array. I'm not really clear on the differences between $groups, "$groups", ${groups}and "${groups}". If anyone could explain it, I would appreciate it.

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

As an extra question - does anyone know the accepted way to refer to these encapsulations?

作为一个额外的问题 - 有没有人知道引用这些封装的公认方式?

回答by ThisSuitIsBlackNot

Braces ($varvs. ${var})

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

In most cases, $varand ${var}are the same:

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

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

The braces are only needed to resolve ambiguity in expressions:

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

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

Quotes ($varvs. "$var"vs. "${var}")

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

When you add double quotes around a variable, you tell the shell to treat it as a single word, even if it contains whitespaces:

当您在变量周围添加双引号时,您会告诉 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

Contrast that behavior with the following:

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

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

As with $varvs. ${var}, the braces are only needed for disambiguation, for example:

$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

Note that "${var}bar"in the second example above could also be written "${var}"bar, in which case you don't need the braces anymore, i.e. "$var"bar. However, if you have a lot of quotes in your string these alternative forms can get hard to read (and therefore hard to maintain). This pageprovides a good introduction to quoting in Bash.

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

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

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

Now for your array. According to the bash manual:

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

Referencing an array variable without a subscript is equivalent to referencing the array with a subscript of 0.

引用一个没有下标的数组变量相当于引用一个下标为 0 的数组。

In other words, if you don't supply an index with [], you get the first element of the array:

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

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

Which is exactly the same as

这与

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

To get all the elements of an array, you need to use @as the index, e.g. ${foo[@]}. The braces are required with arrays because without them, the shell would expand the $foopart first, giving the first element of the array followed by a literal [@]:

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

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

This pageis a good introduction to arrays in Bash.

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

Quotes revisited (${foo[@]}vs. "${foo[@]}")

重新审视行情 ( ${foo[@]}vs. "${foo[@]}")

You didn't ask about this but it's a subtle difference that's good to know about. If the elements in your array could contain whitespace, you need to use double quotes so that each element is treated as a separate "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

Contrast this with the behavior without double quotes:

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

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

回答by Todd A. Jacobs

TL;DR

TL; 博士

All the examples you give are variations on Bash Shell Expansions. Expansions happen in a particular order, and some have specific use cases.

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

Braces as Token Delimiters

大括号作为令牌分隔符

The ${var}syntax is primarily used for delimiting ambiguous tokens. For example, consider the following:

${var}语法主要用于分隔不明确的标记。例如,请考虑以下情况:

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

Braces in Array Expansions

数组扩展中的大括号

The braces are required to access the elements of an arrayand for other special expansions. For example:

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

$ 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

Tokenization

代币化

Most of the rest of your questions have to do with quoting, and how the shell tokenizes input. Consider the difference in how the shell performs word splittingin the following examples:

其余的大部分问题都与引用以及 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

The @symbol interacts with quoting differently than *. Specifically:

@符号与引用不同于交互*。具体来说:

  1. $@"[e]xpands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word."
  2. In an array, "[i]f the word is double-quoted, ${name[*]}expands to a single word with the value of each array member separated by the first character of the IFS variable, and ${name[@]}expands each element of name to a separate word."
  1. $@“[e] 扩展到位置参数,从一个开始。当扩展发生在双引号内时,每个参数扩展为一个单独的词。”
  2. 在数组中,“[i] 如果单词是双引号,则${name[*]}扩展为单个单词,每个数组成员的值由 IFS 变量的第一个字符分隔,并将${name[@]}name 的每个元素扩展为单独的单词。”

You can see this in action as follows:

您可以看到如下操作:

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

$ count_params "$@"
3

$ count_params "$*"
1

The use of a quoted expansion matters a great deal when variables refer to values with spaces or special characters that might prevent the shell from word-splitting the way you intend. See Quotingfor more on how quoting works in Bash.

当变量引用带有空格或特殊字符的值时,使用带引号的扩展很重要,这可能会阻止 shell 以您想要的方式进行分词。有关引用在 Bash 中的工作原理的更多信息,请参阅引用

回答by Jonathan Leffler

You need to distinguish between arrays and simple variables — and your example is using an array.

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

For plain variables:

对于普通变量:

  • $varand ${var}are exactly equivalent.
  • "$var"and "${var}"are exactly equivalent.
  • $var并且${var}完全等效。
  • "$var"并且"${var}"完全等效。

However, the two pairs are not 100% identical in all cases. Consider the output below:

然而,这两对并非在所有情况下都 100% 相同。考虑以下输出:

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

Without the double quotes around the variable, the internal spacing is lost and the expansion is treated as two arguments to the printfcommand. With the double quotes around the variable, the internal spacing is preserved and the expansion is treated as one argument to the printfcommand.

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

With arrays, the rules are both similar and different.

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

  • If groupsis an array, referencing $groupsor ${groups}is tantamount to referencing ${groups[0]}, the zeroth element of the array.
  • Referencing "${groups[@]}"is analogous to referencing "$@"; it preserves the spacing in the individual elements of the array, and returns a list of values, one value per element of the array.
  • Referencing ${groups[@]}without the double quotes does not preserve spacing and can introduce more values than there are elements in the array if some of the elements contain spaces.
  • 如果groups是一个数组,则引用$groups${groups}等同于引用${groups[0]}数组的第零个元素。
  • 引用"${groups[@]}"类似于引用"$@";它保留数组各个元素的间距,并返回一个值列表,数组的每个元素一个值。
  • 引用${groups[@]}没有双引号不保留间距和比有数组中的元素,如果某些元素包含空格可以引入更多的价值。

For example:

例如:

$ 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
$

Using *instead of @leads to subtly different results.

使用*而不是@导致略有不同的结果。

See also How to iterate over the arguments in a bashscript.

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

回答by kojiro

The second sentence of the first paragraph under Parameter Expansionin man bashsays,

Parameter Expansionin下第一段第二句man bash说,

The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.

要扩展的参数名称或符号可以用大括号括起来,这是可选的,但用于保护要扩展的变量免受紧跟其后的字符的影响,这些字符可以解释为名称的一部分。

Which tells you that the name is simply braces, and the main purpose is to clarify where the name begins and ends:

这告诉您名称只是大括号,主要目的是阐明名称的开始和结束位置:

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

If you read further you discover,

如果你进一步阅读,你会发现,

The braces are required when parameter is a positional parameter with more than one digit…

当参数是一个多于一位的位置参数时需要大括号......

Let's test:

让我们测试一下:

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

Huh. Neat. I honestly didn't know that before writing this (I've never had more than 9 positional parameters before.)

呵呵。整洁的。老实说,在写这篇文章之前我不知道(我以前从来没有超过 9 个位置参数。)

Of course, you also need braces to do the powerful parameter expansion features like

当然,你还需要大括号来做强大的参数扩展功能,比如

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

as well as array expansion.

以及数组扩展。

回答by nortally

A related case not covered above. Quoting an empty variable seems to change things for test -n. This is specifically given as an example in the infotext for coreutils, but not really explained:

上面没有涉及的一个相关案例。引用一个空变量似乎改变了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.

I'd love to hear the detailed explanation. My testing confirms this, and I'm now quoting my variables for all string tests, to avoid having -zand -nreturn the same result.

我很想听听详细的解释。我的测试证实了这一点,我现在为所有字符串测试引用我的变量,以避免出现-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

回答by Sierisimo

Well, I know that encapsulation of a variable helps you to work with something like:

好吧,我知道变量的封装可以帮助您处理以下内容:

${groups%example}

or syntax like that, where you want to do something with your variable before returning the value.

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

Now, if you see your code, all the magic is inside

现在,如果你看到你的代码,所有的魔法都在里面

${groups[@]}

the magic is in there because you can't write just: $groups[@]

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

You're putting your variable inside the {}because you want to use special characters []and @. You can't name or call your variable just: @or something[]because these are reserved characters for other operations and names.

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