bash extglob:模式列表中模式的顺序是否重要?

Uwe*_*Uwe 7 bash wildcards

根据 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/
Run Code Online (Sandbox Code Playgroud)

但是如果我们在三个模式列表中的每一个中交换替代品,大部分应该匹配的目录都消失了:

$ ls -ld /@(usr|.)/@(local|.)/@(share|.)/
drwxrwsr-x 10 root staff 4096 Oct 15  2018 /usr/local/share/
Run Code Online (Sandbox Code Playgroud)

与不存在的子目录相同。在这里它有效:

$ 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/
Run Code Online (Sandbox Code Playgroud)

在这里它没有:

$ ls -ld /@(usr|.)/@(foo|.)/@(share|.)/
ls: cannot access '/@(usr|.)/@(foo|.)/@(share|.)/': No such file or directory
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?这种行为是在某处记录的,还是只是简单的错误?(这是 GNU bash,版本 4.4.12(1)。)

mr.*_*tic 2

在 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.
Run Code Online (Sandbox Code Playgroud)

这里对行为的描述有点模糊,但并不意味着“。” 必须位于每个(子)模式的开头,您可以通过以下方式证明:

$ echo  /@(usr|.)/@(local|.)/@(share|.)/
/usr/local/share/
$ echo  /@(usr|..)/@(local|..)/@(share|..)/
/../../../ /usr/../../ /usr/local/../ /usr/local/share/
Run Code Online (Sandbox Code Playgroud)

所以问题是特定于“.”的。并不是 ”..”。

我相信这是 中的一个错误extglob_skipname(),从循环中的第 218 行开始while (t = glob_patscan (pp, pe, '|')) { ... },这种模式中的最后一项没有正确处理(与 中的前导“.”抑制逻辑交互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/
Run Code Online (Sandbox Code Playgroud)

所以答案是子模式顺序不重要,也不重要,但是当最后一项是“.”时,似乎一个错误会导致问题。