regex - grep当前目录下所有.h和.c文件中的关键字但排除两个目录

标签 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 Action ,但它是如何工作的呢?以及为什么它不是 \( -path "./stubdom/*"-o -path "./dist/*"-o\) (我在末尾添加了另一个 -o)。

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

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

最佳答案

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

  2. find 的一般操作是搜索目录列表,并对在搜索表达式计算结果为真的目录下找到的每个名称执行某些操作。

    您当前的命令是:

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

    我将删除 find . 部分,将其视为其余答案的假设。我还将使用名称 AB 代替 stubdomdist 来缩短它一切都是可见的。

    我们当然可以通过用 -name 替换 -regex 来简化它:

    -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 项上的 /* 会适得其反。

为了演示,创建一个垃圾目录,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 个名称是文件在 cd 下,这是需要的。

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 {} +

如果任何文件名或目录名包含空格(或制表符或换行符),这可以避免出现问题。如果您的这些命令版本支持该表示法(GNU 支持;Mac OS X 支持,因此其他 BSD 变体也支持)。

(使用系统 (BSD) find 在 Mac OS X 10.9.1 上完成测试,而不是使用 GNU find) p>

关于regex - grep当前目录下所有.h和.c文件中的关键字但排除两个目录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20871845/

相关文章:

mysql - for 循环中的多个变量

mysql - 使用正则表达式提取 MySQL 中每个单词的第一个字符

regex - 生成基于正则表达式的字符串的所有可能组合的列表

c# - Telnet 响应在 ~50 个字符后中间包含\r\n

linux - 如何格式化 ifconfig 的输出

Bash - 检查多个环境变量并列出所有缺失的变量

javascript - 在 String.split() 中使用捕获组

c# - Makefile 正则表达式替换

linux - 网络服务器 node.js 作为非 root 用户

c - 返回包含数组的结构