为什么在双引号内指定部分路径时 posh 无法执行路径名扩展?

Why does posh fail to perform pathname expansion when a part of the path is specified within double-quotes?

考虑以下简单的 shell 脚本:

rm -rf bar \"bar\"
mkdir -p bar
touch bar/baz
echo "bar"/*

我用 bash、ksh、zsh 和 dash 得到了预期的输出,但我没有得到 它与豪华:

susam@debian:~$ bash foo.sh
bar/baz
susam@debian:~$ ksh foo.sh
bar/baz
susam@debian:~$ zsh foo.sh 
bar/baz
susam@debian:~$ dash foo.sh
bar/baz
susam@debian:~$ posh foo.sh
bar/*

我想了解 posh 的行为是否符合 POSIX 标准,或者它是否是一个错误。

POSIX 文档中的相关部分似乎是“2.6 Word Expansion”:

他们都提到路径名扩展发生在引用删除之前。

  1. Pathname expansion (see Pathname Expansion) shall be performed, unless set -f is in effect.
  2. Quote removal (see Quote Removal) shall always be performed last.

考虑到这一点,豪华行为看起来是正确的,因为 "bar"/* 在删除引号之前与上面的任何路径完全不匹配,因此不会发生路径扩展。

所以这让我怀疑如果有一个字面上名为 "bar" 的目录,即引号是目录名称的一部分,那么 posh 会匹配它。但以下修改过的脚本表明这不是真的。

rm -rf bar \"bar\"
mkdir -p \"bar\"
touch \"bar\"/baz
echo "bar"/*

这是输出:

susam@debian1:~$ bash foo2.sh 
bar/*
susam@debian1:~$ ksh foo2.sh 
bar/*
susam@debian1:~$ zsh foo2.sh 
foo2.sh:3: no matches found: bar/*
susam@debian1:~$ dash foo2.sh 
bar/*
susam@debian1:~$ posh foo2.sh 
bar/*

因此 posh 中的模式 "bar"/* 既不匹配路径 bar/baz 也不匹配路径 "bar"/baz。那匹配什么呢?豪华行为是错误还是功能?

以下是版本详细信息,以防对您有所帮助:

susam@debian:~$ cat /etc/debian_version 
8.3
susam@debian:~$ dpkg -l bash ksh zsh dash posh
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                        Version            Architecture       Description
+++-===========================-==================-==================-============================================================
ii  bash                        4.3-11+b1          amd64              GNU Bourne Again SHell
ii  dash                        0.5.7-4+b1         amd64              POSIX-compliant shell
ii  ksh                         93u+20120801-1     amd64              Real, AT&T version of the Korn shell
ii  posh                        0.12.3             amd64              Policy-compliant Ordinary SHell
ii  zsh                         5.0.7-5            amd64              shell with lots of features

甚至在 quote removal 之前,shell 会将未加引号的 " 视为字符串定界符而不是文字字符。

此外,我无法重现您的测试结果。在这两种情况下,bash 和 posh 都为我提供了相同的结果。

您可以尝试使用 bash --posix(具有 POSIX 行为),您会看到结果是 bar/baz.

重点是 "bar" 的路径名扩展给出了 bar。有关名称扩展的模式匹配的更多详细信息,请参阅 SCL 第 2.13 节。

这是 posh 中的错误 -- 请参阅错误 #636601。从 posh 版本 0.12.6 开始它仍然是开放的。

附在discussion of that bug you'll find a patch。应用它时,posh 的行为类似于 bash(因此在您的第一个示例中,echo "bar"/* 给出 bar/baz)。

此外,bash(和 patchedposh)的行为确实符合 POSIX.标准说

  1. Quote removal (see Quote Removal) shall always be performed last.

这意味着字面意思,作为纯句法删除保护引号的动作最后一步。正如 hek2mgl 所指出的,引号的 语义 含义仍然适用于前面的步骤。 (否则,例如 "*" 这样的引号根本不会有任何效果。)

所以转念一想,这个结论是不正确的:

Considering this, the posh behaviour looks right because "bar"/* does not literally match any path above before the quote removal, so path expansion does not occur.