运行`grep`排除特定路径中的文件


12

我想./test/main.cpp从搜索中排除文件。

这是我所看到的:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

我知道可以通过在管道和过滤器布置中使用多个命令来获得所需的输出,但是是否有一些引号/转义符可以使grep我理解本机?


基于过滤输出的解决方案无法很好地扩展,因为它在排除相关结果之前会不必要地搜索文件。如果要排除整个目录(带有--exclude-dir),则会放大该问题。这就是为什么我想让grep在本地执行排除。
nobar 2015年

1
--exclude指定glob不是路径
PersianGulf

Answers:


6

grep 如果您在不同目录中有更多具有相同名称的文件,则无法对某个目录中的文件执行此操作,请改用find:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


你为什么要逃避\!\+?没有反斜杠,它似乎工作正常。
2015年

@nobar我已经习惯了,因为有些字符是shell关键字,所以您永远不会感到惊讶,因为如果将它们转义就什么也不会发生。
MichalH 2015年

grep无法做到,find取而代之” –完美。
nobar 2015年

4

我认为GNU不可能实现grep。您虽然不需要管道。

find

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

zsh

grep pattern ./**/*~./test/main.cpp(.)

(排除隐藏文件,以及排除.git,.svn ...)。


2

我可以写一本书:“迷失的艺术xargs”。在find ... -exec … ';启动为每个文件的grep(但与变异-exec … +没有)。好吧,这些天我们在浪费CPU周期,为什么不呢?但是,如果性能,内存和功耗是一个问题,请使用xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

GNU的find- -print0NUL终止其输出,xargs' -0选项将采用该格式作为输入。这样可以确保文件中包含任何有趣的字符,管道都不会混淆。该-r选项确保万一find没有发现任何错误。

注意,您现在可以执行以下操作:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep的-z功能与xargs 的功能相同-0


3
一些有趣的注意事项,但我不确定您对性能问题是否正确。据我了解find -exec (cmd) {} +的工作方式相同xargsfind -exec (cmd) {} \;工作方式相同xargs -n1。换句话说,只有\;使用版本,您的陈述才是正确的。
nobar 2015年

3
管道连接的xargs效率比使用效率低-exec … +(尽管是有限的)。这里没有答案甚至都没有提到-exec … \;
吉尔斯(Gilles)'所以

1
好吧 我约会了 感谢您的评论和更正。我以为\ +是拼写错误。哦,看,-exec ... +是在2005年1月添加的。是的,我并没有过时。
奥修斯

2

如果您的find支持-path在2008年添加到POSIX,但是在Solaris中仍然缺少:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
我认为这不会起作用,因为nobar希望main.cpp位于其他目录中
Eric Renouf

1
您的模式也不会从所有其他目录中排除main.cpp吗?这是不希望的
Eric Renouf

@EricRenouf:哦,我的错,是误读。更新了我的答案。
cuonglm

@吉尔斯:为什么-path不是POSIX?
cuonglm

啊,对不起,我的错,它是在2008年添加的。但是,Solaris仍然缺少它。
吉尔斯(Gilles)'SO-不再是邪恶的'

1

作为记录,这是我更喜欢的方法:

grep pattern $(find . -type f ! -path './test/main.cpp')

通过grep在命令开始处保留,我认为这会更清楚-而且它不会禁用grep的颜色突出显示。从某种意义上说,find在命令替换中使用只是扩展/替换功能的(受限)文件搜索子集的一种方式grep


对我来说,find -exec语法有点奥秘。一种复杂性find -exec是(有时)需要转义各种字符(尤其\;是在Bash下使用时)。仅出于将事物放入熟悉的上下文的目的,以下两个命令基本上是等效的:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

如果要排除子目录,则可能需要使用通配符。我在这里不完全了解架构- 谈论arcane

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

进一步概述在脚本中使用的find基于解决方案的注意事项:命令行应包含/ 选项。否则,在的搜索结果中碰巧只有一个文件名的情况下,它将更改输出格式。值得注意的是,如果使用的本机文件搜索(带有选项),则似乎没有必要。grep-H--with-filenamefindgrep-r

...更好的是,将其/dev/null作为第一个文件进行搜索。这解决了两个问题:

  • 它可以确保如果要搜索一个文件,grep将认为有两个文件并使用多文件输出模式。
  • 它可以确保如果没有要搜索的文件,grep将认为只有一个文件并且不会在stdin上挂起。

因此,最终答案是:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

您不应find在命令替换中使用输出。如果文件名包含空格或其他特殊字符,则此操作会中断。使用find -exec,功能强大且易于使用。
吉尔(Gilles)'所以

@Gilles:非常好-输出也可能超出某些程序的命令行大小限制。买者自负。
2015年

啊。“查找”语法非常困难。“ -o”是“或”运算符(在Linux上也是“ -or”),但是它的典型用法(例如,与“ -prune”配合使用)在概念上不映射到逻辑或的概念。它是功能性的,而不是逻辑性的。
nobar

根据名称匹配来排除子目录的另一种方法:find -iname "*target*" -or -name 'exclude' -prune。好吧,这很有效-修剪后的目录将被列出,但不会被搜索。如果您不想将其列出,则可以添加一些多余的内容! -name 'exclude'
-nobar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.