$VARIABLE
的任何表达式都用双引号括起来,至少如果有人希望它被shell解释为一个单项,否则,$VARIABLE
内容中的任何空格都会抛出shell。 /> 但是我知道,在最新版本的Shell中,不再总是需要使用双引号(至少出于上述目的)。例如,在
bash
中:另一方面,在zsh
中,相同的三个命令成功执行。因此,根据此实验,似乎可以在bash
中省略[[ ... ]]
内部的双引号,但不能省略[ ... ]
内部或命令行参数的双引号,而在zsh
中,在所有这些情况下都可以省略双引号。 但是从上述轶事例子中推断出一般规则是一个偶然的命题。很高兴看到何时需要双引号的摘要。我主要对
zsh
,bash
和/bin/sh
感兴趣。#1 楼
首先,将zsh与其余的分开。这与旧的和现代的shell无关:zsh的行为有所不同。 zsh设计师决定使其与传统的shell(Bourne,ksh,bash)不兼容,但更易于使用。其次,始终使用双引号要比记住何时要容易得多是必需的。多数情况下,它们是必需的,因此您需要了解何时不需要它们,而不是何时需要。
简而言之,在单词列表或单词列表的任何地方都需要双引号期望有一种模式。在解析器需要原始字符串的情况下,它们是可选的。
不带引号会发生什么
请注意,如果没有双引号,则会发生两种情况。
首先,扩展结果(用于参数替换的变量的值,例如
${foo}
,或用于命令替换的命令的输出,例如$(foo)
)被分割成单词,无论它包含空格是什么。 br />更精确地说,扩展结果在出现在IFS
变量值(分隔符)中的每个字符处进行分割。如果分隔符序列包含空格(空格,制表符或换行符),则将空格视为单个字符;否则,空格将被视为单个字符。前导,尾随或重复的非空白分隔符会导致空白字段。例如,对于IFS=" :"
,:one::two : three: :four
会在one
之前,one
和two
之间以及three
和four
之间(单个)之间产生空字段。它包含字符\[*?
之一。如果该模式与一个或多个文件名匹配,则该模式将由匹配文件名的列表替换。与通常只取变量
$foo
的值的"$foo"
相比,不带引号的变量扩展foo
通常被称为“ split + glob运算符”。命令替换也是如此:"$(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
语句中。 >请注意,在案例模式中确实需要双引号。在大小写模式中不会发生单词拆分,但是将未加引号的变量解释为模式,而将带引号的变量解释为文字字符串。 />在双括号内。双括号是shell的特殊语法。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语法(这是一个恰好称为=~
的命令) 。请参见单括号或双括号在非交互式POSIX外壳(不是
[ … ]
或[
)中的重定向中。[[ -e $filename ]]
一些外壳在进行交互时会将变量的值视为通配符模式。 POSIX禁止在非交互式shell中执行该行为,但是包括bash(在POSIX模式下除外)和ksh88(包括在某些商业Unices的POSIX
bash
(如Solaris)中发现的)在内的少数shell仍然可以在其中进行操作(尝试拆分,并且重定向失败,除非split + globbing仅导致一个单词),这就是为什么最好在ksh88
脚本中引用重定向目标的原因,以防万一您想将其转换为sh
脚本或在其上运行在该点上bash
不兼容的系统,或者它可能来自交互式shell。在算术表达式内。实际上,您需要省略引号,以便将变量作为算术表达式进行解析。
扩展,因为它们在大多数外壳程序中都受到POSIX要求的(!?)拆分字的影响。
在一些罕见的情况下,不带引号的变量和命令替换可能很有用:想要将这些模式扩展到匹配文件的列表。您想在某个字符处分割一个值:使用
sh
禁用globbing,将bash
设置为分隔符(或不让其在空白处分割),然后进行扩展。Zsh
在zsh中,除少数例外,大多数情况下可以省略双引号。
sh
从不扩展为多个单词,但是,如果$IFS
的值为空字符串,则它将扩展为空列表(与包含单个空单词的列表相对)。对比度: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
类似地,
set -f
扩展到数组的所有元素,而IFS
仅扩展到非空元素。$var
参数扩展标志有时需要在整个替换项前后加上双引号:使用var
可以在一个单词中获得命令的输出,没有最后的换行符。使用"${array[@]}"
来获取命令的确切输出,包括最终的换行符。使用$array
从命令的输出中获取行数组。评论
实际上,您需要省略引号,以便将变量解析为算术表达式。为什么我可以使您的示例使用引号:echo“ $((”“ $ expr”))“
– Cyker
16年11月29日在4:05
这是man bash所说的:将该表达式视为在双引号内,但括号内的双引号未得到特殊处理。
– Cyker
16年11月29日在4:20
此外,对于任何有兴趣的人,split + glob的正式名称是单词拆分和路径名扩展。
– Cyker
16年11月29日在4:21
仅供参考-在StackOverflow上,我有人在此答案中拉出“期望出现原始字符串时可选”语言,以捍卫未引用echo参数。可能有必要尝试使语言更加明确(也许是“当解析器需要原始字符串时”)。
–查尔斯·达菲(Charles Duffy)
17 Mar 10 '17 at 4:01
@CharlesDuffy Ugh,我没有想到这个误读。我已经将“ where”更改为“ when”,并按照您的建议加强了句子。
–吉尔斯'所以-不再是邪恶的'
17 Mar 10 '17 at 11:04
评论
您在zsh中观察到的行为取决于设置,并受SH_WORD_SPLIT选项的影响。另请参见忘记在bash / POSIX shell中引用变量的安全隐患
顺便说一句-变量使用全大写字母的变量名,对操作系统和Shell具有含义; POSIX规范明确建议对应用程序定义的变量使用小写名称。 (虽然所引用的规范专门针对环境变量,但环境变量和shell变量共享一个名称空间:尝试使用环境变量已使用的名称创建shell变量将覆盖后者)。参见pubs.opengroup.org/onlinepubs/009695399/basedefs/…,第四段。