$VAR vs ${VAR} 以及引用或不引用

xen*_*ide 184 shell shell-script environment-variables posix quoting

我可以写

VAR=$VAR1
VAR=${VAR1}
VAR="$VAR1"
VAR="${VAR1}"
Run Code Online (Sandbox Code Playgroud)

最终结果对我来说似乎都差不多。我为什么要写一个或另一个?这些中的任何一个都不是便携式/ POSIX 吗?

Sha*_*off 130

VAR=$VAR1是 的简化版VAR=${VAR1}。第二个可以做第一个不能做的事情,例如引用数组索引(不可移植)或删除子字符串(POSIX 可移植)。请参阅POSIX 规范中的初学者和参数扩展Bash 指南的更多变量部分。

在变量周围使用引号,例如rm -- "$VAR1"orrm -- "${VAR}"是一个好主意。这使得变量的内容成为一个原子单位。如果变量值包含空格(嗯,$IFS特殊变量中的字符,默认情况下为空格)或通配符并且您没有引用它,那么每个单词都被考虑用于文件名生成(通配符),其扩展会为您提供尽可能多的参数正在做。

$ find .
.
./*r*
./-rf
./another
./filename
./spaced filename
./another spaced filename
./another spaced filename/x
$ var='spaced filename'
# usually, 'spaced filename' would come from the output of some command and you weren't expecting it
$ rm $var
rm: cannot remove 'spaced': No such file or directory
# oops! I just ran 'rm spaced filename'
$ var='*r*'
$ rm $var
# expands to: 'rm' '-rf' '*r*' 'another spaced filename'

$ find .
.
./another
./spaced filename
./another spaced filename
$ var='another spaced filename'
$ rm -- "$var"
$ find .
.
./another
./spaced filename
Run Code Online (Sandbox Code Playgroud)

关于可移植性:根据POSIX.1-2008 section 2.6.2,花括号是可选的。

  • @Shawn:在作业中*不需要*引号。它们在大多数其他用途中都是必需的,包括 `export VAR=$VAR1`。至于大括号,它们是可选的(检查您引用的部分的第四段;在所有 pre-POSIX 和 POSIX shell 中都是这种情况)。 (11认同)
  • @shawn 更新了我的问题,因为我也对可移植性感到好奇 (2认同)

Gil*_*il' 71

${VAR}并且$VAR完全等效。对于普通的变量扩展,使用的唯一原因${VAR}是解析会在变量名中抓取太多字符,如${VAR1}_$VAR2(没有大括号相当于${VAR1_}$VAR2)。大多数修饰的扩展 ( ${VAR:=default}, ${VAR#prefix}, ...) 需要大括号。

在变量赋值中,字段拆分(即在值中的空白处拆分)和路径名扩展(即通配)被关闭,因此VAR=$VAR1完全等同于VAR="$VAR1", 在所有 POSIX shell 和所有我听说过的 pre-POSIX sh 中. (POSIX 参考:简单命令)。出于同样的原因,VAR=*可靠地设置VAR为文字字符串*;当然VAR=a b设置VAR为a因为首先b是一个单独的词。一般来说,在 shell 语法需要一个单词的地方,双引号是不必要的,例如incase … in(但不是在模式中),但即使在那里你也需要小心:例如 POSIX 指定重定向目标( >$filename) 不需要在脚本中引用,但包括 bash 在内的一些 shell 确实需要双引号,即使在脚本中也是如此。请参阅何时需要双引号?进行更彻底的分析。

在其他情况下,您确实需要双引号,特别export VAR="${VAR1}"是export "VAR=${VAR1}"在许多 shell 中(可以等效地写成)(POSIX 使这种情况保持打开状态)。这种情况与简单赋值的相似性,以及不需要双引号的情况列表的分散性质,就是为什么我建议只使用双引号的原因,除非您确实想要拆分和通配符。

  • 作为一般规则,即使我知道值不包含任何“IFS”字符,我也会始终引用变量扩展,因为我想养成这个习惯。一个例外是我在进行变量赋值时不引用值(除非需要,例如当值包含空格时)。当存在诸如`FOO=$(BAR=$(BAZ=blah; printf %s "${BAZ}"); printf %s "${BAR}") 之类的命令替换时,这使得编辑器语法突出显示更有用。我没有将所有内容着色为“字符串”颜色,而是获得嵌套代码的语法突出显示。这也是我避免反引号的原因。 (3认同)

SYA*_*iDE 12

引述

考虑双引号用于变量扩展,而单引号用于强引用,即无扩展。

扩张:

this='foo'
that='bar'
these="$this"
those='$that'
Run Code Online (Sandbox Code Playgroud)

输出:

for item in "$this" "$that" "$these" "$those"; do echo "$item"; done
foo
bar
foo
$that
Run Code Online (Sandbox Code Playgroud)

值得一提的是,出于多种原因,您应该尽可能使用引用,其中最好的原因是它被认为是最佳实践,并且为了可读性。也因为 Bash 有时很古怪,而且经常以看似不合逻辑或不合理/出乎意料的方式出现,并且引用将隐式期望更改为显式期望,从而减少了错误面(或其中的可能性)。

虽然不引用是完全合法的,并且在大多数情况下都可以使用,但提供该功能是为了方便,并且可能不太便携。保证反映意图和期望的完全正式的做法是引用。

代换

现在还要考虑该构造"${somevar}"用于替换操作。几个用例,例如替换和数组。

更换(剥离):

thisfile='foobar.txt.bak'
foo="${thisfile%.*}"   # removes shortest part of value in $thisfile matching after '%' from righthand side
bar="${thisfile%%.*}"  # removes longest matching

for item in "$foo" "$bar"; do echo "$item"; done
foobar.txt
foobar
Run Code Online (Sandbox Code Playgroud)

更换(更换):

foobar='Simplest, least effective, least powerful'
# ${var/find/replace_with}
foo="${foobar/least/most}"   #single occurrence
bar="${foobar//least/most}"  #global occurrence (all)

for item in "$foobar" "$foo" "$bar"; do echo "$item"; done
Simplest, least effective, least powerful
Simplest, most effective, least powerful
Simplest, most effective, most powerful
Run Code Online (Sandbox Code Playgroud)

数组:

mkdir temp
# create files foo.txt, bar.txt, foobar.txt in temp folder
touch temp/{foo,bar,foobar}.txt
# alpha is array of output from ls  
alpha=($(ls temp/*))

echo "$alpha"         #  temp/foo.txt
echo "${alpha}"       #  temp/foo.txt
echo "${alpha[@]}"    #  temp/bar.txt  temp/foobar.txt  temp/foo.txt
echo "${#alpha}"      #  12 # length of first element (implicit index [0])
echo "${#alpha[@]}"   #  3  # number of elements
echo "${alpha[1]}"    #  temp/foobar.txt # second element
echo "${#alpha[1])"   #  15 # length of second element

for item in "${alpha[@]}"; do echo "$item"; done
temp/bar.txt
temp/foobar.txt
temp/foo.txt
Run Code Online (Sandbox Code Playgroud)

所有这些都只是触及"${var}"替代结构的表面 。Bash shell 脚本的权威参考是 libre 在线参考,TLDP The Linux Documentation Projecthttps://www.tldp.org/LDP/abs/html/parameter-substitution.html

  • 非常翔实。 (3认同)