Linux和Unix文件通配符的区别

4

我希望能够获取Linux目录中以大写字母开头的文件列表。在Unix中,这很简单:

ls [A-Z]*

然而,在Linux中,我看到的匹配似乎不区分大小写:

=> ls
A.txt  b.txt  B.txt  c.txt  C.txt

=> ls [A]*
A.txt

=> ls [AB]*
A.txt  B.txt

=> ls [ABC]*
A.txt  B.txt  C.txt

=> ls [A-C]*

在命令行中输入以上命令,会列出所有以大写字母 A 至 C 开头的文件,并且文件名可以包含任意字符。

=> ls [b]*
b.txt

=> ls [a-c]*

A.txt b.txt B.txt c.txt

在Unix端运行相同的命令时,结果与预期相同。这是Linux一直以来的行为吗?虽然可以使用awk轻松解决此问题,但我想知道是否之前从未注意到这一点。谢谢。


在你的输出中没有看到任何特别的东西...请澄清一下(至少命令和输出似乎在某些地方混淆了)。还要注意,这不是操作系统的问题(Linux vs Unix vs...),而是shell变体和配置的问题。 - Jean-Baptiste Yunès
没错,我本来期望[A-C]不包括b或c,但根据答案来看,这是环境变量的影响(正如你所指出的,并非Linux与Unix之间的区别)。 - Fitz Bushnell
1个回答

7
结果取决于不同的 shell 选项,特别是:nocasematch 和 nocaseglob。还要考虑 LC_COLLATE 变量(在 sort、[-] 等中使用)。
$ shopt extglob nocasematch nocaseglob
extglob         off
nocasematch     off
nocaseglob      off


$ printf "%s\n" a A b B c C | sort
a
A
b
B
c
C

因此,[A-C]范围包含b和c,但不包括a,同样[a-c]应该包括A但不包括C。

$ printf "%s\n" a A b B c C | LC_COLLATE=C sort
A
B
C
a
b
c

会得到不同的结果。

$ LC_COLLATE=C ls [A-C]*

应该返回预期结果,这个语法只为新的进程执行(ls)设置变量,而不是在当前 shell 进程中。

编辑:感谢评论,之前的命令是错误的,因为扩展在 LC_COLLATE 设置之前被处理,最简单的方法是拆分成两个命令。

$ export LC_COLLATE=C
$ ls [A-C]*

2
LC_COLLATE=C ls [A-C]* 的例子中,LC_COLLATE 被设置为执行 ls 时的环境变量 -- 但是通配符会在执行之前被展开。这也是为什么 s=hello echo $s 不按人们的期望工作的原因。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接