在“ls”命令中使用通配符查找仅包含大写字母的文件

在“ls”命令中使用通配符查找仅包含大写字母的文件

因此,我一直在研究文件系统,并想知道是否列出/etc名称中仅包含大写字母的文件。我命令

ls *[A-Z]* 

但控制台也显示包含较低字符的文件。我只想使用ls命令。控制台程序区域设置是否相关?

根本原因是什么?

答案1

[A-Z]并不意味着大写。它表示从A到 的字母Z,其中可能包含小写字母。通常你应该使用[[:upper:]]。 (即使没有 .,这也可以在 Bash 中工作extglob。)

匹配哪些字符[A-Z]取决于您的区域设置。

已澄清您想要显示在任何地方至少包含大写字符的所有文件名 - 不仅是完全由大写字符组成的文件名 - 而且当您使用 时ls *[A-Z]*,您会得到一些不包含大写字符的文件名任何其中的大写字符。

当您的语言环境的词典顺序穿插有大写和小写字母时(例如,AaBbCcDd...),就会发生这种情况。尽管您可以设置其他区域设置(例如,LC_ALL=C),但最佳解决方案通常是编写专门匹配大写字母的模式。

哪些字符是大写字母不同区域设置之间也可能有所不同,但如果某些内容在您的区域设置中是大写字母,那么您可能希望将其包含在内。所以这可能是一个优点[[:upper:]]而不是一个缺点。

代替使用[[:upper:]]

大多数 Bourne 风格的 shell(例如 Bash)都支持 glob 中的 POSIX 字符类。此命令将列出/etc名称中至少有一个大写字母的条目:

ls -d /etc/*[[:upper:]]*

您获得的某些条目可能是目录。如果您想显示其内容而不仅仅是列出目录,那么您可以删除该-d标志。您可能还想--在模式之前放置一个标志,以防/etc其中有以 开头的条目-。但你可能不知道。 (在脚本中,您通常需要--在此处使用。)

您可能不想要点文件,但如果您想要......

这不会显示以 开头的条目.。通常你不想展示给他们看。如果您确实需要它们,大多数 shell 允许您编写一个也与它们匹配的 glob,或者配置 globbing 以默认包含它们。.在 Bash 中自动包含前导条目的选项是dotglob,并且可以使用 启用它shopt -s dotglob。对于其他外壳请参见 。或者你可以简单地为它们编写第二个 glob:

ls -d /etc/*[[:upper:]]* /etc/.*[[:upper:]]*

大多数流行的 Bourne 风格 shell 支持大括号扩展,因此您可以以更少的重复编写得更紧凑:

ls -d /etc/{,.}*[[:upper:]]*

在包括 Bash 在内的大多数 shell 中,当您编写两个单独的 glob 时,如果其中一个没有展开,您将收到一条错误消息 - 因为大多数 shell 中的默认行为是不展开它。但ls仍会显示与另一个匹配的条目。但正如斯特凡·查泽拉斯(Stéphane Chazelas)指出的,在一些 shell 中,包括非常流行的 Zsh,整个命令会失败并且ls永远不会运行。如果您以交互方式使用 shell,这并不是真正有害,因为您可以修改命令并再次运行它,但这种结构不适合可移植脚本。如果您设置了 shell 选项,Bash 也会以这种方式运行failglob

为此,您不需要扩展通配符。

在 Bash 中,您不需要启用扩展通配符即可在通配符模式中使用 POSIX 字符类。在我的 Bash 4.3.48 系统上:

ek@Io:~$ shopt extglob
extglob         off
ek@Io:~$ ls -d /etc/*[[:upper:]]*
/etc/ConsoleKit     /etc/LatexMk         /etc/ODBCDataSources  /etc/UPower
/etc/ImageMagick-6  /etc/NetworkManager  /etc/rcS.d            /etc/X11

但你确实需要它来匹配文件名仅有的大写字母。

你什么如果您想匹配包含以下内容的文件名,则需要扩展通配符仅有的大写字母。然后你可以使用+([[:upper:]])or *([[:upper:]]),这些都是扩展的全局变量。

如果您使用的是 Bash,请参阅本文,本指南,3.5.8.1 模式匹配在里面GNU Bash 手册了解详情。也可以看看斯特凡·查泽拉斯的回答

答案2

对于仅由大写字母组成的文件名。

(如FOO, ÉTÉ, ΛΈΞΗ;不同于FOO.BAR, ÉTÉ(其后ÉEU+0301 结合锐音符号 1))

kshzsh -o kshglob -o nobareglobqualbash -O extglob:

ls -d +([[:upper:]])

With zsh -o extendedglob(你宁愿使用它而不是kshglob):

ls -d [[:upper:]]#

或者使用 GNU ls(假设文件名仅包含有效字符):

ls --ignore='*[^[:upper:]]*'

或者用find代替ls(这里只是输出它的参数,我希望你想使用像-l它这样有用的选项):

find . ! -name . -prune -name '*' ! -name '*[^[:upper:]]*'

(这-name '*'是为了过滤掉包含无效字符的文件名,下一个! -name将无法过滤掉这些文件名(find至少在某些实现中))

对于不带小写字母的文件名

(但仍然允许使用非字母,如 in ABC.TXT),其中ksh

(FIGNORE='@(.|..|*[[:lower:]]*)'; ls -d -- *)

bash -O dotglob -O extglobzsh -o kshglob -o dotglob -o nobareglobqual

ls -d -- !(*[[:lower:]]*)

或者zsh -o extendedglob

ls -d -- ^*[[:lower:]]*(D)

或者使用 GNU ls(假设文件名仅包含有效字符):

ls -A --ignore='*[[:lower:]]*' --ignore='.*[[:lower:]]*'

(事实上​​,--ignore='.*[[:lower:]]*'需要额外的东西对我来说似乎是一个错误)

find

find . ! -name . -prune -name '*' ! -name '*[[:lower:]]*'

(对于某些find实现,不包括具有无效字符的文件名,即使有效字符都不是小写字符)。

对于至少包含一个大写字母的文件名:

(像Foo.bar,,.Été.txt不像123.6foo.bar

使用zsh -o dotglobor bash -O dotglobdotglob包含名称以 开头的文件.):

ls -d -- *[[:upper:]]*

find

find . ! -name . -prune -name '*[[:upper:]]*'

(对于某些find实现,不包括具有无效字符的文件名,即使某些有效字符是大写字符)


1 要允许使用 组合字符,zsh -o pcrematch您可以使用类似 perl 的正则表达式,利用 Unicode 字符属性:

ls -d -- *(e@'[[ $REPLY =~ "^(?>\p{Lu}\pM*)*$" ]]'@)

答案3

请原谅我之前的回答,我还没喝咖啡。

我不确定是否仅使用 ls 来执行此操作。但是,这里还有另一个 grep 可以解决这个问题:

LS | egrep ^[^a-z0-9]*$

答案4

为什么你只想使用 ls?您可以简单地使用 find 来代替:
find -regex './[A-Z]+'

编辑:
根据man 7 glob

通配符匹配
如果字符串包含字符“?”、“*”或“[”之一,则该字符串是通配符模式。通配符是将通配符模式扩展为与该模式匹配的路径名列表的操作。匹配定义为:

A '?' (不在括号之间)匹配任何单个字符。

'*'(不在方括号之间)匹配任何字符串,包括空字符串。

字符类

表达式“[...]”,其中前导“[”之后的第一个字符不是“!”匹配单个字符,即方括号括起来的任何字符。

常用表达
请注意,通配符模式不是正则表达式,尽管它们有点相似。首先,它们匹配文件名,而不是文本,其次,约定不一样:例如,在正则表达式中“*”表示前面的内容的零个或多个副本。

现在正则表达式具有括号表达式,其中否定由“^”表示,POSIX 已声明通配符模式“[^...]”的效果未定义。

如果您想使用 ls 您必须记住 bash 不会以与find -regexpor相同的方式翻译您的“*” grep。 *[AZ]* 将尝试匹配任何字符串,后跟大写字母,后跟任何字符串,所以基本上是任何字符串。

相关内容