echo [[:digit:]] 出现意外行为

echo [[:digit:]] 出现意外行为

我想问:

为什么echo {1,2,3}扩展为 1 2 3 这是预期的行为,而echo [[:digit:]]返回[[:digit:]]而我期望它打印从0到 的所有数字9

答案1

因为它们是两种不同的东西。这{1,2,3}是一个例子大括号扩展。这{1,2,3}被扩展通过外壳echo甚至在看到它之前。如果使用以下命令,您可以看到会发生什么set -x

$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3

如您所见,该命令echo {1,2,3}扩展为:

echo 1 2 3

然而,[[:digit:]]是一个POSIX 字符类。当你把它交给echo,shell 也会首先处理它,但这次它被作为一个壳球。它的工作方式与运行相同,echo *它将打印当前目录中的所有文件。 But[[:digit:]]是一个可以匹配任何数字的 shell glob。现在,在 bash 中,如果 shell glob 不匹配任何内容,它将扩展到自身:

$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files

如果 glob 确实匹配某些内容,则会打印:

$ echo /e*c
+ echo /etc
/etc

在这两种情况下,echo只打印 shell 告诉它打印的任何内容,但在第二种情况下,由于 glob 匹配某些内容 ( /etc),所以它被告知打印该内容。

因此,由于您没有任何名称由一位数字(这是匹配的数字[[:digit:]])组成的文件或目录,因此 glob 会扩展到自身,您将得到:

$ echo [[:digit:]]
[[:digit:]]

现在,尝试创建一个名为的文件5并运行相同的命令:

$ echo [[:digit:]]
5

如果有多个匹配文件:

$ touch 1 5       
$ echo [[:digit:]]
1 5

这(在某种程度上)记录在关闭此行为的选项man bash的解释中:nullglob

nullglob
    If  set,  bash allows patterns which match no files (see
    Pathname Expansion above) to expand to  a  null  string,
    rather than themselves.

如果您设置此选项:

$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]]  ## prints nothing

$ 

答案2

{1,2,3}大括号扩展,它扩展到列出的单词,而不考虑其含义。

[...]是一个字符组,用于文件名扩展(或通配符,或通配符)类似于星号*和问号?。它匹配其中列出的任何单个字符,或属于命名组成员的字符(例如[:digit:]列出的字符)。大多数 shell 的默认行为是,如果没有与其匹配的文件,则将通配符保留原样。

(请注意,您无法真正将通配符/模式转换为它要匹配的字符串集。星号可以匹配任何长度的任何字符串,因此扩展包含它的任何模式将产生无限的字符串列表。)

所以:

$ bash -c 'echo [[:digit:]]'           # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]'            # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]'           # now there are two matches
1 3                                    # note that d, i, g and t do NOT match

但仍然:

$ bash -c 'echo {1,2,3}'
1 2 3

两者都由 shell 扩展ls,无论您运行的命令是、 或echo或,都没有关系rm。另请注意,如果引用其中任何一个,它们将不会被扩展:

$ bash -c 'echo "[[:digit:]]"'         # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}

答案3

{1,2,3}(例如{1..3}大括号扩展。它们在命令执行之前由 shell 解释。

[[:digit:]]是一个模式匹配令牌,但您没有在任何与该模式匹配的文件的位置使用它。如果您使用没有匹配项的模式匹配,它将扩展到自身:

$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3

相关内容