首先,将zsh与其余的分开。这与旧的和现代的shell无关:zsh的行为有所不同。zsh设计师决定使其与传统外壳(Bourne,ksh,bash)不兼容,但更易于使用。
其次,始终记住使用双引号要比记住何时需要双引号容易得多。大多数时候它们都是必需的,因此您将需要学习何时不需要它们,而不是何时需要它们。
简而言之,在需要单词或模式列表的任何地方都需要双引号。在解析器需要原始字符串的情况下,它们是可选的。
没有引号会发生什么
请注意,如果没有双引号,则会发生两件事。
- 首先,扩展结果(用于参数替换的变量的值,如
${foo}
,或用于命令替换的命令的输出,如$(foo)
)包含空格的任何地方都会被拆分为单词。
更准确地说,扩展结果在出现在IFS
变量值(分隔符)中的每个字符处被分割。如果分隔符序列包含空格(空格,制表符或换行符),则将空格视为单个字符;否则,空格将被视为单个字符。前导,尾随或重复的非空白分隔符会导致空白字段。例如,使用IFS=" :"
,:one::two : three: :four
之前产生空字段one
,之间one
和two
之间,和(单个的)three
和four
。
- 如果拆分产生的每个字段包含一个字符,则将其解释为glob(通配符模式)
\[*?
。如果该模式与一个或多个文件名匹配,则该模式将由匹配文件名列表替换。
一个无引号的变量扩展$foo
俗称“ split + glob运算符”,与之相反,它只"$foo"
取变量值foo
。命令替换"$(foo)"
也是如此:是命令替换,$(foo)
是后跟split + glob的命令替换。
在哪里可以省略双引号
在Bourne样式的shell中,我可以想到所有这些情况,您可以在其中编写不带双引号的变量或命令替换,并且按字面值解释值。
在作业的右侧。
var=$stuff
a_single_star=*
请注意,您确实需要在后面加上双引号export
,因为它是普通的内置函数,而不是关键字。这仅在某些shell中才是正确的,例如dash,zsh(在sh仿真中),yash或posh;bash和ksh都export
特别对待。
export VAR="$stuff"
在case
声明中。
case $var in …
请注意,在案例模式中确实需要双引号。区分大小写不会在大小写模式中发生,但是将未加引号的变量解释为模式,而将带引号的变量解释为文字字符串。
a_star='a*'
case $var in
"$a_star") echo "'$var' is the two characters a, *";;
$a_star) echo "'$var' begins with a";;
esac
在双括号内。双括号是shell的特殊语法。
[[ -e $filename ]]
除了在需要模式或正则表达式的地方确实需要双引号之外:在=
or ==
或!=
or 的右侧=~
。
a_star='a*'
if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
fi
您确实需要像往常一样在单括号内加上双引号,[ … ]
因为它们是普通的shell语法(这是一个恰好被称为的命令[
)。请参阅单括号或双括号
在非交互式POSIX Shell中进行重定向(不是bash
,也不是ksh88
)。
echo "hello world" >$filename
一些外壳在进行交互时会将变量的值视为通配符模式。POSIX禁止在非交互式外壳中执行该操作,但是包括bash(在POSIX模式下除外)和ksh88(包括在sh
某些商业Unices的POSIX(据称是Solaris)的POSIX 中发现的)在内的一些Shell仍在该外壳中进行操作(bash
也尝试拆分)除非该重定向无法割裂+通配符在只有一个字的结果),这就是为什么它是最好用重定向的目标在sh
脚本如果你想将它转换为bash
有一天脚本,或运行的系统上,其中sh
的不符合规定的在这一点上,也可来源于从交互shell。
在算术表达式内。实际上,您需要省略引号,以便将变量解析为算术表达式。
expr=2*2
echo "$(($expr))"
但是,您确实需要在算术扩展周围加上引号,因为在大多数外壳程序中,引号都会引起分词,因为POSIX要求使用(!?)。
在关联数组下标中。
typeset -A a
i='foo bar*qux'
a[foo\ bar\*qux]=hello
echo "${a[$i]}"
在某些罕见情况下,不带引号的变量和命令替换可能很有用:
- 当变量值或命令输出由全局模式列表组成,并且您要将这些模式扩展到匹配文件列表时。
- 当您知道该值不包含任何通配符时,则该值
$IFS
未被修改,并且您希望将其拆分为空白字符。
- 当您想在某个字符处分割值时:使用禁用globlob
set -f
,将其设置IFS
为分隔符(或将其保留以在空白处分割),然后进行扩展。
sh
在zsh中,除了少数例外,大多数情况下可以省略双引号。
$var
从不扩展为多个单词,但是如果的值为var
空字符串,则它将扩展为空列表(与包含单个空单词的列表相对)。对比:
var=
print -l $var foo # prints just foo
print -l "$var" foo # prints an empty line, then foo
同样,"${array[@]}"
扩展为数组的所有元素,而$array
仅扩展为非空元素。
该@
参数扩展标志有时需要围绕整个替换双引号:"${(@)foo}"
。
如果不加引号,则命令替换将进行字段拆分:echo $(echo 'a'; echo '*')
打印a *
(用单个空格),而echo "$(echo 'a'; echo '*')"
打印未修改的两行字符串。使用"$(somecommand)"
得到命令的输出就是一个字,SANS最终换行符。使用"${$(somecommand; echo _)%?}"
获得包括最后的换行命令的确切输出。用于"${(@f)$(somecommand)}"
从命令的输出获取行的数组。
SH_WORD_SPLIT
选项影响。