普通字符是一种匹配自身的模式。它可以是支持字符集中的任何字符,除了 NUL、Quoting 中需要引用的那些特殊 shell 字符以及以下三个特殊模式字符。匹配应基于用于编码字符的位模式,而不是基于字符的图形表示。如果引用了任何字符(普通、shell 特殊或模式特殊),则该模式应与字符本身匹配。shell 特殊字符总是需要引用。
据我了解,该模式["!"a]
将匹配!
和中的任何一个a
。这也是我尝试过的大多数 shell 中的行为,除了zsh
和ksh93
:
$ for shell in /bin/*[^c]sh; do
printf '=%-17s=\n' "$shell"
"$shell" -c 'case a in ["!"a]) echo 1;; esac'
done
=/bin/ash =
1
=/bin/bash =
1
=/bin/dash =
1
=/bin/heirloom-sh =
1
=/bin/ksh =
=/bin/lksh =
1
=/bin/mksh =
1
=/bin/pdksh =
1
=/bin/posh =
1
=/bin/schily-osh =
1
=/bin/schily-sh =
1
=/bin/yash =
1
=/bin/zsh =
Run Code Online (Sandbox Code Playgroud)
zsh
并且ksh93
似乎对待["!"a]
与 相同[!a]
,它匹配任何字符,除了a
:
$ for shell in ksh93 zsh; do
printf '=%-6s=\n' "$shell"
"$shell" -c 'case b in ["!"a]) echo 1;; esac'
done
=ksh93 =
1
=zsh =
1
Run Code Online (Sandbox Code Playgroud)
是否有任何原因(历史,发展,...)zsh
和ksh93
那样做的?
zsh
在两者ksh
和sh
仿真中做同样的事情。
busybox sh
, Solaris/usr/xpg4/bin/sh
和 FreeBSDsh
也表现得像 POSIX 文档。
ksh88
也像大多数其他 shell 一样,行为在kssh88
和之间发生了变化ksh93
:
$ ksh88 -c 'case a in ["!a"]) echo yes; esac'
yes
$ ksh88 -c 'case b in ["a-c"]) echo yes; esac'
$
Run Code Online (Sandbox Code Playgroud)
你引用的那段话并不代表你所说的意思。
匹配单个字符的模式
(...) 一个普通字符是一个匹配自身的模式。(...) 如果引用了任何字符(普通、shell 特殊或模式特殊),则该模式应与字符本身匹配。
所有这些仅适用于在模式中代表自己的字符。这不适用于出现在预期模式字符之外的上下文中的字符。特别是,它不适用于括号表达式内。括号表达式的语法在以下条目中描述[
:
如果一个开放括号引入一个括号表达式,如XBD RE Bracket Expression,(...)
(我省略了关于!
vs 的部分^
用于补充。)RE 方括号表达式的描述没有说明任何关于引用的内容(这并不奇怪,因为它通常是关于方括号表达式,而不是关于 shell 脚本中模式中的方括号表达式)。
根据对 POSIX.1-2008 的严格解释,不清楚该模式["!"a]
应该匹配什么。一种解释是它应该匹配任何字符"
,!
或者a
: 该字符"
在括号表达式中没有特殊含义。我在规范中找不到任何会使这种解释无效的内容。另一种解释是"
保留其引用行为,但这意味着括号表达式的内容是!a
,并且由于没有对括号表达式内的引用字符进行特殊处理,该集合是 all-but- a
。我在 POSIX 规范中找不到对您的解释(以及破折号、bash 和其他 shell 的行为)的任何支持。这当然是有道理的,但这不是文字所说的。
通过为此添加一些措辞,POSIX 的未来版本可以强制您进行解释。例如,[
可以将描述更改为
如果开括号引入了XBD RE Bracket Expression 中的括号表达式,除了 \ 字符 (
'!'
) 应替换 \ 字符 ('^'
) 在正则表达式表示法中的非匹配列表中的作用,它应引入模式括号expression ,并且被引用的任何字符都应代表其自身作为括号表达式、整理元素或类表达式的元素。以未加引号的 \ 字符开头的括号表达式会产生未指定的结果。否则,'['
应匹配字符本身。
鉴于 POSIX 主要是描述性的而不是规范性的,我希望这种破坏 ksh(通常是参考 shell)的更改只包含在标准的主要更新中,并且现有版本的任何缺陷至少允许现有的不同解释。