grep当前目录下所有.h和.c文件中的关键字,但不包括两个目录

时间:2014-01-01 18:31:28

标签: regex linux bash shell

我想在当前目录.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操作!)

但是,我不太明白为什么会这样。我有几个问题:

  1. \( -path "./stubdom/*" -o -path "./dist/*" \)这是一个find的动作,但它是如何工作的?以及为什么它不是\( -path "./stubdom/*" -o -path "./dist/*" -o \)(我在最后添加了另一个-o)。

  2. 如果我将-regex放在-type之前,它会打印出.o文件,这意味着-regex如果放在-type之前就不起作用1}}。 我的问题是: find命令的选项是否有从左到右的执行顺序?

  3. 是否有更简洁的方法来实现我的目标:在当前目录下的所有.h.c文件中grep一个关键字,但排除两个目录?

4 个答案:

答案 0 :(得分:6)

  1. -o运算符是'或'运算符。第二条路径之后的-o需要在其后进行另一次测试。带括号的表达式也受条件-type d-prune的约束。总的来说,该术语表示“如果当前名称是目录,并且路径与路径表达式匹配,则搜索被修剪”,这意味着搜索不会继续

  2. find的一般操作是它搜索目录列表,并对搜索表达式求值为true的目录下找到的每个名称执行某些操作。

    您当前的命令是:

    find . -type d \( -path "./stubdom/*" -o -path "./dist/*" \) -prune -o -regex '.*\.\(h\|c\)$' -print
    

    我将放弃find .部分,将其视为答案的其余部分。我还将使用名称AB代替stubdomdist来缩短它,以便一切都可见。

    我们当然可以将-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)
    

    因此,出现目标文件名的原因是无条件地应用了打印。

  3. 我的实验表明,/*条款-path会适得其反。

  4. 要演示,请在其中创建一个垃圾目录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)

我在这里试着回答你:

  1. 我不确定你为什么要在最后添加另一个-o。如果\( -path "./stubdom/*" -o -path "./dist/*" \)True中的任何一个匹配,则-path "./stubdom/*"行评估为-path "./dist/*"-o是逻辑OR,它是二元运算符,因此需要两个参数。如果没有别的东西,你不能在最后追加它。
  2. 您可能忘记移动-o。如果您未在OR-type d之间放置-regex ...,则查找仅查找与正则表达式匹配的目录。而不是用于匹配正则表达式的目录的任何内容。顺便说一句,是的,为了找到选项的顺序绝对相关。
  3. 我认为你的解决方案已经足够好了。
  4. 总结你的线如何工作,它等同于这个伪代码:

    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)

  1. \( -path "./stubdom/*" -o -path "./dist/*" \)-prune的过滤器,因此应排除这些目录。它不能是\( -path "./stubdom/*" -o -path "./dist/*" -o \),这可能是一个错误。
  2. 如果你这样做,find实际上还没有搜索到,所以匹配约束被丢弃。
  3. grep还有排除文件的选项(例如--exclude-dir等)。

答案 3 :(得分:0)

您还可以尝试以下命令:

find | awk '(! (/stubdom\// || /dist\//)) && /\.(c|h)$/ {
    r=system ("grep -q map_foreign_range "$0)
    if(!r) print
}'