kjo*_*kjo 147 shell bash zsh shell-script quoting
过去的旧建议是对任何涉及 a 的表达式加双引号$VARIABLE,至少在希望 shell 将其解释为单个项目的情况下,否则,内容中的任何空格$VARIABLE都会脱离 shell。
但是,我知道在较新版本的 shell 中,不再总是需要双引号(至少出于上述目的)。例如,在bash:
% FOO='bar baz'
% [ $FOO = 'bar baz' ] && echo OK
bash: [: too many arguments
% [[ $FOO = 'bar baz' ]] && echo OK
OK
% touch 'bar baz'
% ls $FOO
ls: cannot access bar: No such file or directory
ls: cannot access baz: No such file or directory
Run Code Online (Sandbox Code Playgroud)
在zsh,而另一方面,同样的三个命令成功。因此,基于此实验,似乎在 中bash可以省略 内部的双引号[[ ... ]],但不能省略内部[ ... ]或命令行参数中zsh的双引号,而在 中,在所有这些情况下都可以省略双引号。
但是从像上面这样的轶事例子中推断出一般规则是一个偶然的命题。很高兴看到何时需要双引号的摘要。我主要兴趣zsh,bash和/bin/sh。
Gil*_*il' 171
首先,将 zsh 与其他部分分开。这不是旧壳与现代壳的问题:zsh 的行为不同。zsh 设计者决定使其与传统 shell(Bourne、ksh、bash)不兼容,但更易于使用。
其次,始终使用双引号比记住何时需要它们容易得多。大多数时候都需要它们,因此您需要在不需要它们时学习,而不是在需要它们时学习。
简而言之,在需要单词列表或模式的任何地方都需要双引号。在解析器需要原始字符串的上下文中,它们是可选的。
请注意,如果没有双引号,则会发生两件事。
${foo},或命令替换的命令输出$(foo))被拆分为包含空格的单词。IFS变量值(分隔符)中出现的每个字符处进行拆分。如果分隔符序列包含空格(空格、制表符或换行符),则该空格算作单个字符;前导、尾随或重复的非空白分隔符会导致空字段。例如,具有IFS=" :",:one::two : three: :four 产生空字段之前one,之间one和two,和(单个的)之间three和four。\[*?。如果该模式与一个或多个文件名匹配,则该模式将替换为匹配文件名列表。不带引号的变量扩展$foo通俗地称为“split+glob 运算符”,与此相反,它只"$foo"获取变量的值foo。命令替换"$(foo)"也是如此:是命令替换,$(foo)是命令替换后跟 split+glob。
以下是我在 Bourne 风格的 shell 中能想到的所有情况,您可以在其中编写没有双引号的变量或命令替换,并按字面解释值。
在作业的右侧。
var=$stuff
a_single_star=*
Run Code Online (Sandbox Code Playgroud)
请注意,在 之后确实需要双引号export,因为它是一个普通的内置函数,而不是关键字。这仅适用于某些 shell,例如 dash、zsh(在 sh 仿真中)、yash 或 posh;bash 和 ksh 都export特别对待。
export VAR="$stuff"
Run Code Online (Sandbox Code Playgroud)在一份case声明中。
case $var in …
Run Code Online (Sandbox Code Playgroud)
请注意,在 case 模式中确实需要双引号。分词不会在 case 模式中发生,但不带引号的变量被解释为模式,而带引号的变量被解释为文字字符串。
a_star='a*'
case $var in
"$a_star") echo "'$var' is the two characters a, *";;
$a_star) echo "'$var' begins with a";;
esac
Run Code Online (Sandbox Code Playgroud)双括号内。双括号是 shell 的特殊语法。
[[ -e $filename ]]
Run Code Online (Sandbox Code Playgroud)
除了在需要模式或正则表达式的地方确实需要双引号:在=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
Run Code Online (Sandbox Code Playgroud)
您确实需要像往常一样在单括号内使用双引号,[ … ]因为它们是普通的 shell 语法(这是一个恰好被调用的命令[)。请参见单括号或双括号
在非交互式 POSIX shell 中的重定向(不是bash,也不是ksh88)。
echo "hello world" >$filename
Run Code Online (Sandbox Code Playgroud)
某些 shell 在交互时确实将变量的值视为通配符模式。POSIX 禁止在非交互式 shell 中的这种行为,但一些 shell 包括 bash(POSIX 模式除外)和 ksh88(包括当被发现为sh一些商业 Unices 如 Solaris的(据称)POSIX时)仍然在那里这样做(bash也确实尝试拆分和重定向失败,除非该拆分+通配符在只有一个字的结果),这就是为什么它是最好用重定向的目标在sh脚本如果你想将它转换为bash有一天脚本,或在系统上运行它哪里sh是不符合规定的在这一点上,也可来源于从交互shell。
在算术表达式中。事实上,您需要去掉引号,以便将变量解析为算术表达式。
expr=2*2
echo "$(($expr))"
Run Code Online (Sandbox Code Playgroud)
但是,您确实需要算术扩展周围的引号,因为它们在大多数 shell 中都会按照 POSIX 的要求进行分词(!?)。
在关联数组下标中。
typeset -A a
i='foo bar*qux'
a[foo\ bar\*qux]=hello
echo "${a[$i]}"
Run Code Online (Sandbox Code Playgroud)不带引号的变量和命令替换在一些罕见的情况下很有用:
$IFS被修改,您希望将其拆分为空白字符。set -f,设置IFS为分隔符(或单独保留以在空格处拆分),然后进行扩展。在 zsh 中,大多数情况下您可以省略双引号,但有一些例外。
$var从不扩展为多个单词,但如果 的值为var空字符串,则扩展为空列表(与包含单个空单词的列表相反)。对比:
var=
print -l $var foo # prints just foo
print -l "$var" foo # prints an empty line, then foo
Run Code Online (Sandbox Code Playgroud)
同样,"${array[@]}"扩展到数组的所有元素,而$array只扩展到非空元素。
该@参数扩展标志有时需要围绕整个替换双引号:"${(@)foo}"。
如果未加引号,命令替换会进行字段拆分:echo $(echo 'a'; echo '*')打印a *(带有单个空格)而echo "$(echo 'a'; echo '*')"打印未修改的两行字符串。用于"$(somecommand)"在单个单词中获取命令的输出,没有最后的换行符。使用"${$(somecommand; echo _)%?}"获得包括最后的换行命令的确切输出。用于"${(@f)$(somecommand)}"从命令的输出中获取行数组。
| 归档时间: |
|
| 查看次数: |
61440 次 |
| 最近记录: |