【问题标题】:When do we need curly braces around shell variables?我们什么时候需要在 shell 变量周围加上花括号?
【发布时间】:2012-02-03 15:37:26
【问题描述】:

在shell脚本中,我们什么时候在扩展变量时使用{}

例如,我看到了以下内容:

var=10        # Declare variable

echo "${var}" # One use of the variable
echo "$var"   # Another use of the variable

有显着差异,还是只是风格?一个比另一个更受欢迎吗?

【问题讨论】:

    标签: bash shell syntax curly-braces


    【解决方案1】:

    在这个特定的例子中,它没有任何区别。但是,如果要在字符串中扩展变量 foo${} 中的 {} 很有用

    "${foo}bar"
    

    因为"$foobar" 会改为扩展由foobar 标识的变量。

    在以下情况下也无条件需要花括号:

    • 扩展数组元素,如${array[42]}
    • 使用参数扩展操作,如${filename%.*}(删除扩展名)
    • 将位置参数扩展到 9 之外:"$8 $9 ${10} ${11}"

    在任何地方都这样做,而不仅仅是在潜在的模棱两可的情况下,可以被认为是良好的编程习惯。这既是为了保持一致性,也是为了避免像 $foo_$bar.jpg 这样的意外,在这种情况下,下划线成为变量名的一部分在视觉上并不明显。

    【讨论】:

    • {} 被称为brace expansion${} 被称为变量扩展。他们做不同的事情。除了没有扩展位之外,我会支持你。
    • @NewUser "所以除了数组之外,它并不是真正需要的" 不是这样,大括号对于PARAMETER EXPANSION 是必需的,这是脚本中非常有用的构造。我见过很多 sed 和 awk 脚本,它们可以用一点参数扩展来代替。
    • @caffinatedmonkey $() 用于执行命令,这样md5sum=$(md5sum foo.bin) 会将md5sum foo.bin 的输出存储在变量md5sum 中,现在可以使用${md5sum} 访问。此外,+1 和更多精神上的 OP 提到它是明确的好习惯!
    • @L0j1k 说到明确性,我觉得很重要的一点是$()subshell 执行它的命令。
    • @karatedog ${1:-20} 是参数扩展的一种形式。这里并不明显,因为它主要使用数字和算术运算符,这会欺骗我们认为涉及算术,但它实际上是指位置参数$1,如果未定义将替换为默认值20(语法是${variable:-default_value})。
    【解决方案2】:

    变量在没有${} 的情况下被声明和赋值。你必须使用

    var=10
    

    分配。为了从变量中读取(换句话说,“扩展”变量),您必须使用$

    $var      # use the variable
    ${var}    # same as above
    ${var}bar # expand var, and append "bar" too
    $varbar   # same as ${varbar}, i.e expand a variable called varbar, if it exists.
    

    这有时让我感到困惑 - 在其他语言中,我们以相同的方式引用变量,无论它是在赋值的左侧还是右侧。但是 shell-scripting 不同,$var=10 不会像你想象的那样做!

    【讨论】:

      【解决方案3】:

      您使用{} 进行分组。大括号是取消引用数组元素所必需的。示例:

      dir=(*)           # store the contents of the directory into an array
      echo "${dir[0]}"  # get the first entry.
      echo "$dir[0]"    # incorrect
      

      【讨论】:

      • 我看不懂第一行dir=(*)。据我所知,dir 是一个用于列出目录内容的内置命令(相当于ls -C -b)。你能解释一下吗?
      • 在 shell 编程中,命令和参数必须用空格隔开。在这里,您会看到没有空格的等号,这意味着这是一个变量赋值。 dir是变量名,括号用来将文件名扩展*收集成一个数组。
      • @Jarvis 在这种情况下,单词 dir 除了作为接收赋值的变量之外没有任何意义。您可以通过使用 foo 作为变量来看到这一点。 foo=(*); echo "${foo[2]}"
      【解决方案4】:

      您还可以在大括号内进行一些文本操作:

      STRING="./folder/subfolder/file.txt"
      echo ${STRING} ${STRING%/*/*}
      

      结果:

      ./folder/subfolder/file.txt ./folder
      

      STRING="This is a string"
      echo ${STRING// /_}
      

      结果:

      This_is_a_string
      

      你说得对,“正则变量”是不需要的……但对调试和阅读脚本更有帮助。

      【讨论】:

        【解决方案5】:

        变量名的结尾通常用空格或换行符表示。但是,如果我们在打印变量值后不想要空格或换行符怎么办?花括号告诉 shell 解释器变量名的结尾在哪里。

        经典示例 1) - 没有尾随空格的 shell 变量

        TIME=10
        
        # WRONG: no such variable called 'TIMEsecs'
        echo "Time taken = $TIMEsecs"
        
        # What we want is $TIME followed by "secs" with no whitespace between the two.
        echo "Time taken = ${TIME}secs"
        

        示例 2) 带有版本化 jar 的 Java 类路径

        # WRONG - no such variable LATESTVERSION_src
        CLASSPATH=hibernate-$LATESTVERSION_src.zip:hibernate_$LATEST_VERSION.jar
        
        # RIGHT
        CLASSPATH=hibernate-${LATESTVERSION}_src.zip:hibernate_$LATEST_VERSION.jar
        

        (弗雷德的回答已经说明了这一点,但他的例子有点太抽象了)

        【讨论】:

          【解决方案6】:

          根据 SierraX 和 Peter 关于文本操作的建议,大括号 {} 用于将变量传递给命令,例如:

          假设您有一个 sposi.txt 文件,其中包含一部著名的意大利小说的第一行:

          > sposi="somewhere/myfolder/sposi.txt"
          > cat $sposi
          

          输出:quel ramo del lago di como che volge a mezzogiorno

          现在创建两个变量:

          # Search the 2nd word found in the file that "sposi" variable points to
          > word=$(cat $sposi | cut -d " " -f 2)
          
          # This variable will replace the word
          > new_word="filone"
          

          现在将 word 变量内容替换为 sposi.txt 文件中的 new_word 之一

          > sed -i "s/${word}/${new_word}/g" $sposi
          > cat $sposi
          

          输出:quel filone del lago di como che volge a mezzogiorno

          “ramo”一词已被替换。

          【讨论】:

          • 这在变量周围没有花括号的情况下同样有效。
          • 您可能想要修复weel-known novel 位。尽管如此,还是投了赞成票。
          【解决方案7】:

          访问数组元素和执行大括号扩展总是需要花括号。

          最好不要过分谨慎,使用{} 进行shell 变量扩展,即使没有歧义范围也是如此。

          例如:

          dir=log
          prog=foo
          path=/var/${dir}/${prog}      # excessive use of {}, not needed since / can't be a part of a shell variable name
          logfile=${path}/${prog}.log   # same as above, . can't be a part of a shell variable name
          path_copy=${path}             # {} is totally unnecessary
          archive=${logfile}_arch       # {} is needed since _ can be a part of shell variable name
          

          所以,最好把这三行写成:

          path=/var/$dir/$prog
          logfile=$path/$prog.log
          path_copy=$path
          

          这绝对更具可读性。

          由于变量名不能以数字开头,shell 不需要在编号变量周围使用{}(如$1$2 等),除非这种扩展后跟数字。这太微妙了,确实可以在这种情况下明确使用{}

          set app      # set $1 to app
          fruit=$1le   # sets fruit to apple, but confusing
          fruit=${1}le # sets fruit to apple, makes the intention clear
          

          见:

          【讨论】:

          • It's good to be not over-cautious:我想知道大多数人的想法。始终使用花括号,这样您就不会在需要时忘记它们,或者仅在需要时使用它们,以提高可读性。
          • 我认为缺乏意识导致程序员即使在不需要它们时也会使用花饰。这种无知类似于另一个常见的错误,即不使用双引号来防止无意的分词或通配符。从根本上说,现实情况是程序员对 shell 脚本的重视程度不如 Python 和 Ruby 等其他脚本语言。
          • 确实如此。我最讨厌的是每个人似乎都认为所有变量都应该在 shell 脚本中全部大写 :)
          • 我不同意“不要过度谨慎”的说法;过于谨慎是绝对更好的。我宁愿有一百万个不必要的大括号,而不是一个破坏某些东西的错误,特别是考虑到在 shell 脚本中查找错误是多么困难(无用的错误消息,或者根本没有错误)。
          猜你喜欢
          • 2022-01-19
          相关资源
          最近更新 更多