我想在当前目录.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
之前就不起作用1}}。
我的问题是:
find命令的选项是否有从左到右的执行顺序?
是否有更简洁的方法来实现我的目标:在当前目录下的所有.h
和.c
文件中grep一个关键字,但排除两个目录?
答案 0 :(得分:6)
-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
来缩短它,以便一切都可见。
我们当然可以将-regex
替换为-name
:
-type d \( -path "./A/*" -o -path "./B/*" \) -prune -o -name '*.[ch]' -print
请注意,条件之间的默认连接是'和'。使用C或shell表示法&&
和||
,我们可以看到表达式的形式如下:
(-type d && ( ... ) && -prune) || (-name '*.[ch]' && -print)
当您在-regex
之前移动-name
(现在-type
)时,您将表达式重写为:
(-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
部分,然后删除wc
,显示16个名称是c
和{{1}下的文件那些是想要的。
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
的{{1}}选项来解决此问题,如果您的这些命令的版本支持该符号(GNU确实如此;那么Mac OS X,因此也可能是其他BSD变种。
(在Mac OS X 10.9.1上使用系统(BSD)-0
进行测试,而不是使用GNU xargs
。)
答案 1 :(得分:2)
我在这里试着回答你:
-o
。如果\( -path "./stubdom/*" -o -path "./dist/*" \)
和True
中的任何一个匹配,则-path "./stubdom/*"
行评估为-path "./dist/*"
。 -o
是逻辑OR
,它是二元运算符,因此需要两个参数。如果没有别的东西,你不能在最后追加它。-o
。如果您未在OR
和-type d
之间放置-regex ...
,则查找仅查找与正则表达式匹配的目录。而不是用于匹配正则表达式的目录或的任何内容。顺便说一句,是的,为了找到选项的顺序绝对相关。总结你的线如何工作,它等同于这个伪代码:
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
选项的评论。试试吧。它可能是更简洁的解决方案。
答案 2 :(得分:0)
\( -path "./stubdom/*" -o -path "./dist/*" \)
是-prune
的过滤器,因此应排除这些目录。它不能是\( -path "./stubdom/*" -o -path "./dist/*" -o \)
,这可能是一个错误。find
实际上还没有搜索到,所以匹配约束被丢弃。grep
还有排除文件的选项(例如--exclude-dir
等)。答案 3 :(得分:0)
您还可以尝试以下命令:
find | awk '(! (/stubdom\// || /dist\//)) && /\.(c|h)$/ {
r=system ("grep -q map_foreign_range "$0)
if(!r) print
}'