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

cdmah0mi  于 2023-02-24  发布在  Shell
关注(0)|答案(7)|浏览(181)

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

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}"之间的区别。如果有人能解释一下,我将不胜感激。
作为一个额外的问题-有人知道公认的方式来指这些封装吗?

ndh0cuux

ndh0cuux1#

支架($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[@]}

现在看看您的数组。根据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
vd8tlhqk

vd8tlhqk2#

靶区; DR

你给出的所有例子都是Bash Shell扩展的变体。扩展以特定的顺序发生,有些有特定的用例。

大括号作为标记分隔符

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

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

阵列扩展中的大括号

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

$ 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执行单词拆分的不同之处:

$ 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]从1开始扩展到位置参数。如果扩展出现在双引号内,则每个参数扩展为一个单独的单词。"
    1.在数组中,"如果单词被双引号引起来,${name[*]}将扩展为一个单词,每个数组成员的值由IFS变量的第一个字符分隔,而${name[@]}将name的每个元素扩展为一个单独的单词。"
    您可以在下面的操作中看到这一点:
$ count_params () { echo $#; }
$ set -- foo bar baz 

$ count_params "$@"
3

$ count_params "$*"
1

当变量引用带有空格或特殊字符的值时,使用带引号的扩展非常重要,因为这些空格或特殊字符可能会阻止shell按照您想要的方式进行单词拆分。

ltskdhd1

ltskdhd13#

你需要区分数组和简单变量--而你的例子使用的是数组。
对于普通变量:

  • $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[@]}"类似于引用"$@";它保留数组中各个元素的间距,并返回一个值列表,数组中每个元素对应一个值。
  • 引用不带双引号的${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

zyfwsgd6

zyfwsgd64#

man bash中的 * 参数扩展 * 下第一段的第二句话为:
要展开的参数名称或符号可以用大括号括起来,大括号是可选的,但用于保护要展开的变量,使其后面紧跟的字符不会被解释为名称的一部分。
它告诉您名称只是 braces,主要目的是澄清名称的开始和结束位置:

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

如果你继续读下去你会发现,
当parameter是具有多个数字的位置参数时,需要使用大括号...
让我们测试一下:

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

真不错,在写这篇文章之前我真的不知道(我以前从来没有使用过超过9个位置参数)。
当然,您还需要大括号来执行强大的参数扩展功能,如

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

以及阵列扩展。

ifmq2ha2

ifmq2ha25#

上面没有提到的一个相关案例。引用一个空变量似乎会改变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
nnvyjq4y

nnvyjq4y6#

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

${groups%example}

或者类似的语法,你想在返回值之前对变量做些什么。
现在,如果你看到你的代码,所有的魔法都在里面

${groups[@]}

魔力就在那里,因为你不能只写:$groups[@]
您将变量放在{}中,因为您希望使用特殊字符[]@。您不能仅命名或调用变量:@something[],因为它们是为其他操作和名称保留的字符。

nukf8bse

nukf8bse7#

如果var是变量的名称,则$var${var}相同。
当parameter是具有多个数字的位置参数时,或者当parameter后面跟有不能解释为其名称一部分的字符时,需要使用大括号。
因此,"$var""${var}"是相同的。
但是$var"$var"是不同的,Bash会为$varWord Splitting,但不会为"$var"做。
shell扫描参数展开、命令替换和算术展开的结果,如果这些结果没有出现在双引号中,则进行单词拆分。
注:变量赋值时不进行分词:https://www.gnu.org/software/bash/manual/html_node/Shell-Parameters.html
变量可以由以下形式的语句赋值
name=[value]
如果没有指定值,则将空字符串赋给变量。所有值都经过波浪符扩展、参数和变量扩展、命令替换、算术扩展和引号删除(请参阅Shell参数扩展)。如果变量设置了整型属性,则值被计算为算术表达式,即使$不使用((...))扩展(请参阅算术扩展)。不执行字拆分和文件名扩展。

相关问题