echo [[:digit:]] 的意外行为

Abd*_*aat 10 shell wildcards

我想问:

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

ter*_*don 34

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

$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Run Code Online (Sandbox Code Playgroud)

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

echo 1 2 3
Run Code Online (Sandbox Code Playgroud)

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

$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Run Code Online (Sandbox Code Playgroud)

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

$ echo /e*c
+ echo /etc
/etc
Run Code Online (Sandbox Code Playgroud)

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

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

$ echo [[:digit:]]
[[:digit:]]
Run Code Online (Sandbox Code Playgroud)

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

$ echo [[:digit:]]
5
Run Code Online (Sandbox Code Playgroud)

如果有多个匹配文件:

$ touch 1 5       
$ echo [[:digit:]]
1 5
Run Code Online (Sandbox Code Playgroud)

这(有点)记录在关闭此行为man bashnullglob选项的解释中:

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

如果设置此选项:

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

$ 
Run Code Online (Sandbox Code Playgroud)

  • 另请参阅`shopt -s failglob` 以获得类似于`zsh` 或`fish` 等现代shell 的更有用的行为。 (4认同)

ilk*_*chu 14

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

但仍然:

$ bash -c 'echo {1,2,3}'
1 2 3
Run Code Online (Sandbox Code Playgroud)

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

$ bash -c 'echo "[[:digit:]]"'         # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
Run Code Online (Sandbox Code Playgroud)