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

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

【问题讨论】:

标签: bash shell variables syntax


【解决方案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 ${var}bar
    # 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 "${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 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
    

    【讨论】:

    【解决方案4】:

    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] 扩展为位置参数,从一个开始。当扩展出现在双引号内时,每个参数扩展为一个单独的单词。"
    2. 在数组中,“[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 "${foo}bar"
        barbar
        

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

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

        让我们测试一下:

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

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

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

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

        以及数组扩展。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-28
          • 2019-09-21
          • 2018-12-07
          • 1970-01-01
          • 1970-01-01
          • 2011-06-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多