在grep命令中排除文件名模式

33
我阅读了使用grep --exclude/--include语法来排除某些文件的grep,但是在我的CentOS6.4上,当我执行
grep --exclude=*.cmd ckim * -r

我看到很多匹配 *.cmd 文件的行。
所以似乎 exclude 选项对我不起作用。
问题出在哪里?
当然,我可以像这样做。

grep ckim \`find . -name \*.c -print\`

但是我想知道为什么grep不起作用。


你需要包含 pattern 参数。 - Avinash Raj
6个回答

56

你可以引用这个模式:

grep -r --exclude="*.cmd"  "ckim" ./

附注: ./ 表示当前目录。


1
这不会有显著的差异,除非 OP 已经设置了 nullglob--exclude=*.cmd 不太可能匹配单个文件,并且将保留为参数。这是正确的做法,但它并不能解决问题。 - konsolebox
@konsolebox,我必须承认我对nullglob不是很了解,但是shopt说它是关闭的,但仍然可以工作。我有什么遗漏吗? - Tiago Lopo
它应该能够工作,但即使不加引号也是如此。实际上,我的假设可能是错误的,你的代码实际上可能有助于解决问题 - 不是因为你帮助它防止扩展到文件,而是因为你帮助防止在启用nullglob时被置空。由于引用,任何类型的路径名扩展都被阻止了。我简直不敢相信我真的忽略了那一部分。 - konsolebox

3

使用 . 作为路径规范,而不是 *

grep -r --exclude \*.cmd ckim .

1
在我的测试中,即使使用 *,该命令仍然有效。显式文件参数仍然会被过滤。 - konsolebox
@konsolebox:在Ubuntu上确认。 - Cyrus
我认为使用 -r. 更清晰,而且不会导致 shell 扩展的意外行为。 - Timofey Stolbov
@konsolebox:在RHEL7上使用ACK - Cyrus

1
我看到了很多来自*.cmd文件的grepped行。所以似乎排除选项对我不起作用。 有一个称为nullglob的shell选项,它控制着当没有匹配的文件时,shell模式的扩展。
所以,考虑以下环境:
sh$ touch f.cmd g.sh
sh$ mkdir sub
sh$ echo ckim > sub/h.cmd
sh$ echo ckim > sub/i.cmd

在我的系统上(未设置nullglob),以下命令:
grep --exclude=*.cmd ckim * -r

被 shell 解释为扩展("understood"):

grep --exclude=*.cmd ckim f.cmd g.sh sub -r

也就是说,我将递归地(-r)搜索以f.cmdg.shsub开头的字符串skim,但排除与模式“*.cmd”匹配的任何文件。
结果是:
# nullglob is unset
sh$ grep --exclude=*.cmd ckim * -r
sub/i.sh:ckim

但是,如果您的环境中设置了nullglob选项,则相同的命令会扩展为:

grep ckim f.cmd g.sh sub -r

请注意,整个--exclude=...已经消失了。 因此结果是:
# nullglob is set
sh$ grep --exclude=*.cmd ckim * -r
sub/i.sh:ckim
sub/h.cmd:ckim

现在,来解释一下。当shell遇到一个glob模式(即包含*或?或其他特殊字符的模式)时,它会将其与匹配的文件扩展。但是,如果没有匹配的文件,它会让模式保持不变(如果未设置nullglob),或者用“nothing”替换它(如果设置了nullglob)。
这里的模式是--include=*.cmd(整个字符串因为其中没有空格)。如果您出奇迹般地有一个匹配此模式的文件,它将被替换。否则,它要么保持原样,要么完全删除 - 这取决于nullglob。
您可以轻松地显示、设置(-s)或取消设置(-u)当前bash的nullglob选项的状态:
sh$ shopt nullglob
nullglob        on

sh$ shopt -u nullglob
sh$ shopt nullglob
nullglob        off

sh$ shopt -s nullglob
sh$ shopt nullglob
nullglob        on

1
你也可以像这样做。
grep -rn ckim * | grep -v '\.cmd'

1
这个程序存在不必要的行为,想象一下文件中的某一行是 something.cmd,即使文件名不包含 \.cmd,它也不会显示该行。 - Tiago Lopo
是的,它有一些不必要的行为...但是在同一行上使用“.cmd”,ckim发生的可能性会非常小。 - Ajay

1

多文件的最短代码(注意以“~”结尾的文件要加上点号)是:

grep -lr --exclude=*.{zip,bak,~} "Hello world!" world/* (wrong code)
grep -lr --exclude=*{zip,bak,~} "Hello world!" world/* (correct code)

ChatGPT 可能会为您提供此特定情况下的错误代码。点号后面没有跟着“~”,就像 nycity.txt~ 那样。


0
如果你想从grep中排除某些文件,你应该使用-l选项。
grep -l --exclude=*.cmd ckim * -r

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