根据 bash 手册,如果extglob
启用,该模式应与(以 分隔)@(pattern-list)
中的任何模式匹配。这里它按预期工作:pattern-list
|
$ shopt -s extglob
$ ls -ld /@(.|usr)/@(.|local)/@(.|share)/
drwxr-xr-x 50 root root 4096 Sep 2 16:39 /./././
drwxr-xr-x 12 root root 4096 Oct 15 2018 /usr/././
drwxrwsr-x 10 root staff 4096 Oct 15 2018 /usr/local/./
drwxrwsr-x 10 root staff 4096 Oct 15 2018 /usr/local/share/
drwxr-xr-x 725 root root 20480 Sep 2 16:42 /usr/./share/
但是,如果我们交换三个模式列表中每个模式中的替代项,则大多数应该匹配的目录都消失了:
$ ls -ld /@(usr|.)/@(local|.)/@(share|.)/
drwxrwsr-x 10 root staff 4096 Oct 15 2018 /usr/local/share/
对于不存在的子目录也是如此。在这里它起作用了:
$ ls -ld /@(.|usr)/@(.|foo)/@(.|share)/
drwxr-xr-x 50 root root 4096 Sep 2 16:39 /./././
drwxr-xr-x 12 root root 4096 Oct 15 2018 /usr/././
drwxr-xr-x 725 root root 20480 Sep 2 16:42 /usr/./share/
而这里却没有:
$ ls -ld /@(usr|.)/@(foo|.)/@(share|.)/
ls: cannot access '/@(usr|.)/@(foo|.)/@(share|.)/': No such file or directory
这里发生了什么?这种行为是否记录在某处,或者只是简单的错误? (这是 GNU bash,版本 4.4.12(1)。)
答案1
在 bash-4.3 之前,“.”术语永远不会匹配。来自 bash(1),v5.0,部分路径名扩展:
When a pattern is used for pathname expansion, the character ``.'' at the start of a name or immediately following a slash must be matched explicitly, unless the shell option dotglob is set. The filenames ``.'' and ``..'' must always be matched explicitly, even if dotglob is set.
这里对行为的描述有点模糊,但它不意思是“.”必须位于每个(子)模式的开头,您可以通过以下方式证明:
$ echo /@(usr|.)/@(local|.)/@(share|.)/
/usr/local/share/
$ echo /@(usr|..)/@(local|..)/@(share|..)/
/../../../ /usr/../../ /usr/local/../ /usr/local/share/
所以问题是特定于“.”的。并不是 ”..”。
我相信这是一个错误extglob_skipname()
,从while (t = glob_patscan (pp, pe, '|')) { ... }
循环中的第 218 行开始,这种模式中的最后一项未正确处理(与 中的前导“.”抑制逻辑交互skipname()
),因此“.”从不匹配,但“..”设法匹配。 (glob_patscan
又名感谢PATSCAN
宏游戏。)
其中任何一个也有效:
$ echo /@(usr|.|)/@(local|.|)/@(share|.|)/
/./././ /usr/././ /usr/./share/ /usr/local/./ /usr/local/share/
$ echo /@(usr|.|.)/@(local|.|.)/@(share|.|.)/
/./././ /usr/././ /usr/./share/ /usr/local/./ /usr/local/share/
所以答案是子模式顺序不重要,也不重要,但是当最后一项是“.”时,似乎一个错误会导致问题。