Bash shell 中的 $var、"$var" 和 "$var" 有啥区别?

Posted

技术标签:

【中文标题】Bash shell 中的 $var、"$var" 和 "$var" 有啥区别?【英文标题】:What is the difference between $var, "$var", and "$var" in the Bash shell?Bash shell 中的 $var、"$var" 和 "$var" 有什么区别? 【发布时间】:2013-08-10 17:20:08 【问题描述】:

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

这是一个例子:

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" 之间的区别。如果有人能解释一下,我将不胜感激。

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

【问题讨论】:

另见How to iterate over arguments in a bash script? 【参考方案1】:

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

$groups%example

或类似的语法,你想在返回值之前对你的变量做一些事情。

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

$groups[@]

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

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

【讨论】:

这并没有指出双引号的非常重要的含义,以及没有它们的代码基本上是如何被破坏的。【参考方案2】:

上面没有涉及的相关案例。引用一个空变量似乎改变了test -n 的情况。这在coreutilsinfo 文本中专门作为示例给出,但没有真正解释:

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

【讨论】:

【参考方案3】:

大括号($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 $varbar
# foobar

行情($var vs. "$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

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

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 "$varbar"; do # Expands to 'for i in "foo barbar"; do...'
    echo $i              #   so runs the loop once
done
# foo barbar

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

数组($var vs. $var[@] vs. $var[@]

现在为您的阵列。根据bash manual:

引用不带下标的数组变量等价于引用下标为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[@]

This page 很好地介绍了 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

【讨论】:

还有一种情况:$var:?,当变量未设置或未设置时会报错。参考:github.com/koalaman/shellcheck/wiki/SC2154 @NamNguyen 如果要谈parameter expansion的其他形式,至少还有十几个:$parameter:-word$parameter:=word$parameter#word$parameter/pattern/string等等.我认为这些超出了这个答案的范围。 其实对双引号的讨论有点不完整。进一步查看***.com/questions/10067266/…【参考方案4】:

TL;DR

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

大括号作为标记分隔符

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

$ var1=foo; var2=bar; var12=12
$ echo $var12
12
$ echo $var12
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

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

    $@ "[e] 扩展为位置参数,从一个开始。当扩展出现在双引号内时,每个参数扩展为一个单独的单词。" 在数组中,“[i]如果单词被双引号括起来,$name[*] 扩展为单个单词,每个数组成员的值由 IFS 变量的第一个字符分隔,$name[@] 扩展每个单词名称元素到一个单独的单词。”

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

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

$ count_params "$@"
3

$ count_params "$*"
1

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

【讨论】:

【参考方案5】:

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

对于普通变量:

$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 就等于引用数组的第0 个元素$groups[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。

【讨论】:

【参考方案6】:

man bash参数扩展下第一段第二句说,

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

这告诉你名字只是大括号,主要目的是明确名字的开始和结束:

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

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

当参数是多于一位的位置参数时,大括号是必需的……

让我们测试一下:

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

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

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

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

以及数组扩展。

【讨论】:

以上是关于Bash shell 中的 $var、"$var" 和 "$var" 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

shell之数组和关联数组

bash 中的变量

Linux bash/sh/shell编程中的if语句应该怎么写

LINUX SHELL AWK 符号问题 awk 'BEGINvar=" ' "$file" ' "ENDprint var;'

shell中的(),{}几种语法用法

shell中的(),{}几种语法用法