列出不匹配某个模式的文件?

78

以下是如何在bash中列出所有符合模式的文件:

ls *.jar

如何列出与模式不匹配的所有文件?即所有不匹配*.jar的文件?


1
这个可能不应该被标记为正则表达式,* 是一个 shell 通配符,而不是正则表达式中的“零或多个量词”。 - Scott Weaver
好观点,尽管我正在学习使用正则表达式可能是获取期望匹配的更佳实践。 - calebds
11个回答

95

使用 egrep 风格的扩展模式匹配。

ls !(*.jar)

这个功能从bash-2.02-alpha1版本开始可用。 必须先启用它。

shopt -s extglob

从bash-4.1-alpha开始,有一个配置选项可以默认启用此功能。


1
太好了,提到了“shopt -s extglob”! - Ogre Psalm33
3
这是正确答案。标记为正确的答案并没有直接使用shell,而是使用了一个附属命令。 - dbn
1
@dbw - 即使我更喜欢这个答案,被采纳的答案对于这个问题也是可以的。OP并没有明确要求直接使用“shell”。 - sancho.s ReinstateMonicaCellio
“egrep-style” 这个词非常误导人,因为 grep 处理的是正则表达式,而这里使用的是 shell 模式。 - Benjamin W.
对我来说没有用。当返回大约2000个项目的长列表时,我得到了“参数列表过长”的错误。我猜这是因为分支展开了它们内部命令的输出。因此,它变成了一个非常长的大约有2000个项目的参数列表。 - Christoforos
由于某些原因,如果我将命令用单引号或双引号括起来,就无法在“watch”中使用它。我会收到意外的标记错误。 - Marinos An

67
ls | grep -v '\.jar$'

例如。


2
@Silver89,例如 ls | grep -v -e '\.one$' -e '\.two$' - Michael Krelin - hacker
8
不要解析 ls 命令的输出结果。 - gniourf_gniourf
6
为提高效率,可以使用命令 ls --ignore='.jar' 避免必须调用另一个程序。 - xizdaqrian
@xizdaqrian,说得好,我不知道那个。但我相信它只适用于GNU。 - Michael Krelin - hacker
1
“ls -1” 并没有什么用,我认为如果输出不是终端,则它实际上会自动发生。问题在于,“被视为有害”的文章本身就是有害的 :) 当解析“ls”输出时,必须意识到所有涉及的影响,但如果知道自己在做什么,那就可以为所欲为 :) - Michael Krelin - hacker
显示剩余2条评论

42

鲜为人知的bash扩展规则:

ls !(*.jar)

3
“bash: !: event not found”的意思是“错误:未找到事件”。您需要做什么取决于上下文,但通常这种错误是由于Shell中的历史命令引用出错而导致的。您可以针对特定的情况尝试不同的解决方案,例如使用单引号来避免历史命令的替换,或者使用反斜线转义特殊字符。 - calebds
10
请在命令行中先执行“shopt -s extglob”,然后再输入该命令。 - Ogre Psalm33

25

使用适当版本的find,您可以做出类似这样的操作,但这有点过度:

find . -maxdepth 1 ! -name '*.jar'
find 命令可以查找文件。参数 . 表示从当前目录开始搜索。选项 -maxdepth 1 表示只搜索一层,即当前目录。! -name '*.jar' 查找所有不符合正则表达式 *.jar 的文件。

虽然对于这个应用程序来说有点过度,但是如果你去掉 -maxdepth 1,就可以轻松地递归搜索所有非 jar 文件或其他内容了。


我喜欢这个,因为它也适用于zsh,而一些bash扩展则不行(至少对于我的zsh技能水平来说是如此:D) - Greg Lyon

18

POSIX定义了不匹配的括号表达式,所以我们可以让Shell为我们扩展文件名。

ls *[!j][!a][!r]

尽管这有一些怪癖,但至少它与任何unix shell兼容。


+1 代表符合 POSIX 标准。结合别名,一个小的 bash/python/ruby 函数,比如 "lsnot",可以使其易于使用。 - Nikkolasg
2
这不匹配长度小于三个字符的内容。 - Benjamin W.

6
另一种方法是使用ls -I标志(忽略模式)来实现。
ls -I '*.jar'

这很简单,对我的工作很好,而其他许多方法都不起作用。 - CTS_AE

6
如果您的ls支持它(man ls),请使用--hide=<PATTERN>选项。在您的情况下:
$> ls --hide=*.jar

不需要解析ls的输出(因为它很糟糕),而且它可以扩展到不显示多种类型的文件。有时我需要查看一个(杂乱无章的)目录中非源代码、非对象、非libtool生成的文件:

$> ls src --hide=*.{lo,c,h,o}

效果很棒。


4

如果你想要排除多个文件扩展名,可以使用管道符|进行分隔,例如:ls test/!(*.jar|*.bar)。让我们试一下:

$ mkdir test
$ touch test/1.jar test/1.bar test/1.foo
$ ls test/!(*.jar|*.bar)
test/1.foo

看了其他答案,你可能需要先运行shopt -s extglob


3
你可以将这个简写为!(*.[bj]ar) - Benjamin W.

3
一种解决方案是 ls -1|grep -v '\.jar$'

1
请不要解析ls的输出,谢谢。 - gniourf_gniourf
6
不要对像解析 ls 或使用 goto 这样的规则持宗教般的态度。有时这些解决方案效果最好。 - Jonathan E. Landrum
1
这不是那种情况:“find . -print0 | grep -z '.jar$'”即使您不想使用“--ignore”或Bash的扩展globbing,它也更好。尽管如此,大多数人不想读整篇文章来理解规则或他们应该做什么,您可以在注释中放置简单的说明和建议,例如“在边缘情况下它不起作用;请改用 find -print0grep -z”。 - Daniel H

1
ls -I "*.jar"
  • -I, --ignore=PATTERN:不列出与 shell 模式匹配的隐含条目
  • 无需在执行任何操作之前即可运行
  • 也可以在 watch 引号内使用:watch -d 'ls -I "*.gz"',不像 watch 'ls !(*.jar)' 会产生错误:sh: 1: Syntax error: "(" unexpected

注意:由于某种原因,在 Centos 中需要在 -I 后引用模式,而在 Ubuntu 中则不需要。


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