为什么在双引号内指定部分路径时 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”:
- POSIX.1-2004: 2.6 Word Expansions
- POSIX.1-2008: 2.6 Word Expansions
他们都提到路径名扩展发生在引用删除之前。
- Pathname expansion (see Pathname Expansion) shall be performed, unless
set -f
is in effect.
- 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.标准说
- 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.
考虑以下简单的 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”:
- POSIX.1-2004: 2.6 Word Expansions
- POSIX.1-2008: 2.6 Word Expansions
他们都提到路径名扩展发生在引用删除之前。
- Pathname expansion (see Pathname Expansion) shall be performed, unless
set -f
is in effect.- 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.标准说
- 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.