[英]grep a keyword in all .h and .c files under current directory but exclude two directories
我想在当前目录下的所有.h
和.c
文件中grep一个关键字./
但在输出中排除两个目录./stubdom
和./dist
。
我搜查,试过并测试了几个命令; 最后我认为一个shell工作:
find . -type d \( -path "./stubdom/*" -o -path "./dist/*" \) -prune -o -regex '.*\.\(h\|c\)$' -print | xargs grep map_foreign_range
此shell正在查找所有.h和.c文件并排除./stubdom/和./dist路径:
find . -type d \( -path "./stubdom/*" -o -path "./dist/*" \) -prune -regex '.*\.\(h\|c\)$' -print | xargs grep map_foreign_range
但是,上面的命令不起作用!
(我在正则表达式之前删除-o以获得AND操作!)
但是,我不太明白为什么会这样。 我有几个问题:
\\( -path "./stubdom/*" -o -path "./dist/*" \\)
这是一个find的动作,但它是如何工作的? 为什么它不是\\( -path "./stubdom/*" -o -path "./dist/*" -o \\)
(我在末尾添加了另一个-o)。
如果我把-regex
的前-type
,它会打印出.o文件,这意味着你-regex
如果它之前把不工作-type
。 我的问题是:find命令的选项有从左到右的执行顺序?
有没有更简洁的方法来实现我的目标:在当前目录下的所有.h
和.c
文件中grep一个关键字,但排除两个目录?
-o
运算符是'或'运算符。 第二条路径之后的-o
需要在它之后进行另一次测试。 带括号的表达式也受条件-type d
和-prune
约束。 总的来说,该术语表示'如果当前名称是目录,并且路径与路径表达式匹配,那么搜索将被修剪',这意味着搜索不会继续
find
的一般操作是它搜索目录列表,并对搜索表达式求值为true的目录下找到的每个名称执行某些操作。
您当前的命令是:
find . -type d \\( -path "./stubdom/*" -o -path "./dist/*" \\) -prune -o -regex '.*\\.\\(h\\|c\\)$' -print
我要放弃这个find .
部分,将其视为其余答案的假设。 我还将使用名称A
和B
代替stubdom
和dist
来缩短它,以便一切都可见。
我们当然可以通过用-name
替换-regex
来简化它:
-type d \\( -path "./A/*" -o -path "./B/*" \\) -prune -o -name '*.[ch]' -print
请注意,条件之间的默认连接是'和'。 使用C或shell表示法&&
和||
,我们可以看到表达式的形式如下:
(-type d && ( ... ) && -prune) || (-name '*.[ch]' && -print)
当您在-type
之前移动-regex
(现在为-name
)时,将表达式重写为:
(-name '*.[ch]' && -type d && ( ... ) && -prune) || (-print)
因此,出现目标文件名的原因是无条件地应用了打印。
我的实验表明, -path
上的/*
是适得其反的。
要演示,创建一个垃圾目录, cd
进入它,然后运行:
mkdir a b c d
for d in a b c d
do
for file in abc def pqr zyz
do
for ext in c h
do cp /dev/null $d/$file.$ext
done
done
done
现在运行:
find . -name '*.[ch]' | wc -l
这给出了答案32。
现在运行:
find . -type d \( -path "./a/*" -o -path "./b/*" \) -prune -o -name '*.[ch]' -print | wc -l
这也给出了32。
删除-path
操作数的/*
部分,然后得到16.删除wc
显示16个名称是c
和d
下的文件,这些是需要的文件。
find . -type d \( -path "./a" -o -path "./b" \) -prune -o -name '*.[ch]' -print
因此,应用于您的场景,您应该能够使用:
find . -type d \( -path "./stubdom" -o -path "./dist" \) -prune -o -name '*.[ch]' -print
但是,您最好完全避免使用xargs
:
find . -type d \( -path "./a" -o -path "./b" \) -prune -o -name '*.[ch]' \
-exec grep map_foreign_range {} +
如果任何文件名或目录名包含空格(或制表符或换行符),则可以避免出现问题。 您也可以解决,随着-print0
足月find
和-0
选项xargs
,如果您对这些命令的版本支持的符号(GNU做,也是如此的Mac OS X,因此可能是其他BSD变体也是如此)。
( 测试在Mac OS X 10.9.1完成与系统(BSD) find
,不与GNU find
。)
在这里,我会尽力回答你:
-o
。 如果任何-path "./stubdom/*"
和-path "./dist/*"
,则行\\( -path "./stubdom/*" -o -path "./dist/*" \\)
将被评估为True
-path "./dist/*"
将匹配。 -o
是一个逻辑OR
,它是一个二元运算符,所以它需要两个参数。 如果没有别的,你不能在最后附加它。 -o
。 如果你没有在-type d
和-regex ...
之间放置一个OR
-regex ...
find将只查找与regexp匹配的目录。 而不是任何目录或匹配正则表达式的东西。 顺便说一句,是的,因为找到选项的顺序绝对相关。 总结你的线如何工作,它等同于这个伪代码:
if(isdir(file) and file != "./stubdom/*" and file != "./dist/*")
print file;
else if (regex(file, '.*\.\(h\|c\)$' and file != "./stubdom/*" and file != "./dist/*")
print file;
编辑:
阅读我记得关于grep的--exclude-dir
选项的评论。 试试吧。 它可能是更简洁的解决方案。
\\( -path "./stubdom/*" -o -path "./dist/*" \\)
是-prune
的过滤器,因此应排除这些目录。 它不能是\\( -path "./stubdom/*" -o -path "./dist/*" -o \\)
,这可能是一个错误。 find
实际搜索,所以匹配约束被丢弃。 grep
还有排除文件的选项(例如--exclude-dir
等)。 您还可以尝试以下命令:
find | awk '(! (/stubdom\// || /dist\//)) && /\.(c|h)$/ {
r=system ("grep -q map_foreign_range "$0)
if(!r) print
}'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.