我是 zsh 的新手,多年来一直是 bash 用户。
在示例 zsh 脚本中,我看到一个测试:
if [ ! -z ${ZSH_MOTD_CUSTOM+x} ]; then
在 bash 中我期望:
if [ ! -z "$ZSH_MOTD_CUSTOM" ]; then
我不明白 zsh 示例中 +x 的含义以及 if 适用于 bash。
${var+string}与 Bourne shell(从 70 年代末开始)和任何 POSIX shell(包括 bash)中的操作符相同。您可以在 zsh 文档中找到它的描述info zsh \'Parameter Expansion\':
\n\n\n
${NAME+WORD}
\n${NAME:+WORD}
\n如果NAME已设置,或者在第二种形式中为非空,则替换\nWORD; 否则什么都不能替代。
那里比较难找,info bash \'Parameter Expansion\'但那里都是一样的。对于sh,你可以检查POSIX 规范(尽管像 bash 手册中一样,你需要注意提到${parameter:+word}\xc2\xb9 中省略冒号的效果的那句话)。
与 bash 和其他类似 Bourne 的 shell 的主要区别在于,${ZSH_MOTD_CUSTOM+x}不加引号不受 split+glob 的影响,因为在 zsh 中,当您确实希望在参数扩展时执行 IFS 分割和/或 globbing 时,您必须明确请求它($=var对于分割,$~var对于通配符(严格来说,将 的内容$var视为模式),$=~var对于两者,这相当于$var在其他 shell 中)。
但这是错误的,因为未引用的扩展仍然会被空删除。但在这种情况下,偶然的情况下,这不会成为问题,并且可能是作者选择编写它[ ! -z $expansion ]而不是[ -n $expansion ]行不通的原因。
if为空(在if未设置的$expansion情况下),变为而不是,因此它不会测试扩展是否非空,而是测试其本身是否为空字符串(显然不是),因此它出于错误的原因实现了正确的结果(测试变量是否已设置)。${ZSH_MOTD_CUSTOM+x}$ZSH_MOTD_CUSTOM[ ! -z $expansion ][ ! -z ][ ! -z \'\' ]-z
在 bash 中,未加引号的内容${ZSH_MOTD_CUSTOM+x}会受到 split+glob 的影响,因此这会更加错误,但因为它只能扩展到空字符串或文字,所以只有当碰巧包含以下内容x时,问题才会出现:$IFSx
$ bash -xc \'IFS=y; [ ! -z ${HOME+x} ]; echo "$?"\'\n+ IFS=y\n+ \'[\' \'!\' -z x \']\'\n+ echo 0\n0\n$ bash -xc \'IFS=x; [ ! -z ${HOME+x} ]; echo "$?"\'\n+ IFS=x\n+ \'[\' \'!\' -z \'\' \']\'\n+ echo 1\n1\n$ zsh -xc \'IFS=x; [ ! -z ${HOME+x} ]; echo "$?"\'\n+zsh:1> IFS=x\n+zsh:1> [ ! -z x \']\'\n+zsh:1> echo 0\n0\nRun Code Online (Sandbox Code Playgroud)\n\nif [ -n "${ZSH_MOTD_CUSTOM+x}" ]\nRun Code Online (Sandbox Code Playgroud)\n甚至在 Bourne shell 中也可以工作,[ -n "$var" ]其中 的值会失败,$var例如=, -gt... 因为扩展只能是x或 空字符串(因此在 的那些古老实现中没有任何有问题的字符串[)。
现在 zsh 有更惯用的方法来检查变量是否已设置,例如:
\nif (( $+ZSH_MOTD_CUSTOM ))\nRun Code Online (Sandbox Code Playgroud)\nwhere$+var扩展为1if$var已设置,0否则。
或者:
\nif [[ -v ZSH_MOTD_CUSTOM ]]\nRun Code Online (Sandbox Code Playgroud)\n\xc3\x80 la ksh (也可在 bash 中找到)。
\n无论如何,[ ! -z "$ZSH_MOTD_CUSTOM" ]本身是一种复杂的编写方式,它[ -n "$ZSH_MOTD_CUSTOM" ]会做一些不同的事情:if 检查变量是否非空,无论它是否被设置。主要区别在于,如果$ZSH_MOTD_CUSTOM已设置,它将返回 false,但返回空值。与${ZSH_MOTD_CUSTOM+x}变体相反,如果未设置变量并且启用了该选项,也会导致错误nounset。
$ zsh -c \'if [ -n "$foo" ]; then echo non-empty; else echo empty; fi\'\nempty\n$ foo= zsh -o nounset -c \'if [ -n "$foo" ]; then echo non-empty; else echo empty; fi\'\nempty\n$ foo= zsh -o nounset -c \'if [ -n "${foo+x}" ]; then echo set; else echo unset; fi\'\nset\n$ zsh -o nounset -c \'if [ -n "$foo" ]; then echo non-empty; else echo empty; fi\'\nzsh:1: foo: parameter not set\n$ zsh -o nounset -c \'if [ -n "${foo+x}" ]; then echo set; else echo unset; fi\'\nunset\n$ zsh -o nounset -c \'if [ -n "${foo-}" ]; then echo non-empty; else echo empty; fi\'\nempty\nRun Code Online (Sandbox Code Playgroud)\n(在最后一个中,我们使用${foo-}which扩展为相同的东西,但避免了(又名)$foo的影响)。nounsetset -u
\xc2\xb9 请注意,在该功能所在的 Bourne shell 中,最初(在 70 年代末的 Unix 第七版中)仅支持不带冒号的版本,带冒号的版本后来出现在 SysIII 中。
\n这是 Bash 中也存在的参数(变量)扩展语法。可能在大多数其他 Bourne 风格的 shell 中也是如此。在您的示例中,zsh 的唯一特殊之处是正在测试的变量。
在 bash 手册页中我们看到两件事。该扩展语法的解释:
${parameter:+word}
Use Alternate Value. If parameter is null or unset, nothing is substituted,
otherwise the expansion of word is substituted.
Run Code Online (Sandbox Code Playgroud)
前面几段,在扩展语法部分的顶部,有一些东西很多人都忽略了:
When not performing substring expansion, using the forms documented below
(e.g., :-), bash tests for a parameter that is unset or null. Omitting
the colon results in a test only for a parameter that is unset.
Run Code Online (Sandbox Code Playgroud)
最后一句是相关部分:Omitting the colon results in a test only for a parameter that is unset. 为了说明这一点,手册页中给出的语法是:
${parameter:+word}
Run Code Online (Sandbox Code Playgroud)
省略冒号后,它是:
${parameter+word}
Run Code Online (Sandbox Code Playgroud)
不同之处在于,后一种语法在所有情况下都会返回word中的值,除非变量未设置(不存在)。所以您在 zsh 脚本中看到的语法:
${ZSH_MOTD_CUSTOM+x}
Run Code Online (Sandbox Code Playgroud)
x如果已定义则返回$ZSH_MOTD_CUSTOM(无论值是什么 - 即使是空字符串),但如果变量未定义则返回空字符串。
最后,您的脚本示例仅测试变量是否存在,而不考虑它包含的值。我通过引用 Bash 手册页来回答,因为您提到您曾经是 bash 用户并且会发现 bash 描述很熟悉。
:-在我(相当长的)职业生涯中见过:=的Bourne 风格的 shell 脚本中,这不是常见的习惯:?用法,因此很多人不知道:+没有:. 我自己最近才了解到它们。
| 归档时间: |
|
| 查看次数: |
477 次 |
| 最近记录: |