【问题标题】:How can I exclude directories matching certain patterns from the output of the Linux 'find' command?如何从 Linux 'find' 命令的输出中排除与某些模式匹配的目录?
【发布时间】:2012-07-12 16:18:26
【问题描述】:

我想将正则表达式与 Linux 的 find 命令一起使用,以递归地进入一个庞大的目录树,向我显示所有 .c、.cpp 和 .h 文件,但忽略包含某些子字符串的匹配项。最终,我想将输出发送到xargs 命令以对所有匹配文件进行某些处理。我可以通过 grep 管道 find 输出以删除包含这些子字符串的匹配项,但该解决方案不适用于包含空格的文件名。所以我尝试使用find 的-print0 选项,它以nul 字符而不是换行符(空格)来终止每个文件名,并使用xargs -0 来期待nul 分隔的输入而不是空格分隔的输入,但我不能'不知道如何通过管道 grep 过滤器成功地通过 nul 分隔的find; grep -Z 在这方面似乎没有帮助。

所以我想我应该为find 编写一个更好的正则表达式,并取消中间的grep 过滤器......也许sed 会是一个替代方案?

无论如何,对于以下目录的小样本...

./barney/generated/bam bam.h
./barney/src/bam bam.cpp
./barney/deploy/bam bam.h
./barney/inc/bam bam.h
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h

...我希望输出包括所有 .h、.c 和 .cpp 文件,但不包括那些出现在“生成”和“部署”目录中的文件。

顺便说一句,您可以创建一个完整的测试目录(名为 fredbarney),通过将整行剪切并粘贴到您的 bash shell 中来测试该问题的解决方案:

mkdir fredbarney; cd fredbarney; mkdir fred; cd fred; mkdir inc; mkdir docs; mkdir generated; mkdir deploy; mkdir src; echo x > inc/dino.h; echo x > docs/info.docx; echo x > generated/dino.h; echo x > deploy/dino.h; echo x > src/dino.cpp; cd ..; mkdir barney; cd barney; mkdir inc; mkdir docs; mkdir generated; mkdir deploy; mkdir src; echo x > 'inc/bam bam.h'; echo x > 'docs/info info.docx'; echo x > 'generated/bam bam.h'; echo x > 'deploy/bam bam.h'; echo x > 'src/bam bam.cpp'; cd ..;

此命令查找所有 .h、.c 和 .cpp 文件...

find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$"

...但是如果我通过 xargs 管道输出其输出,则每个“bam bam”文件都会被视为两个单独的(不存在的)文件名(请注意,这里我只是使用 ls 作为替代我实际上想处理输出):

$ find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$" | xargs -n 1 ls
ls: ./barney/generated/bam: No such file or directory
ls: bam.h: No such file or directory
ls: ./barney/src/bam: No such file or directory
ls: bam.cpp: No such file or directory
ls: ./barney/deploy/bam: No such file or directory
ls: bam.h: No such file or directory
ls: ./barney/inc/bam: No such file or directory
ls: bam.h: No such file or directory
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h

所以我可以使用 -print0 和 -0 参数将其增强为 findxargs

$ find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$" -print0 | xargs -0 -n 1 ls
./barney/generated/bam bam.h
./barney/src/bam bam.cpp
./barney/deploy/bam bam.h
./barney/inc/bam bam.h
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h

...这很好,只是我不想在输出中出现“生成”和“部署”目录。所以我试试这个:

$ find . -regextype posix-egrep -regex ".+\.(c|cpp|h)$" -print0 | grep -v generated | grep -v deploy | xargs -0 -n 1 ls
barney  fred

...这显然行不通。所以我尝试将 -Z 选项与 grep 一起使用(不确切知道 -Z 选项的真正作用),但这也不起作用。所以我想我会为find 写一个更好的正则表达式,这是我能想到的最好的:

find . -regextype posix-egrep -regex "(?!.*(generated|deploy).*$)(.+\.(c|cpp|h)$)" -print0 | xargs -0 -n 1 ls

...但是 bash 不喜欢这样(!。*:未找到事件,无论这意味着什么),即使这不是问题,我的 regex 似乎也无法在 regex tester web 上工作我通常使用的页面。

有什么想法可以让我完成这项工作吗?这是我想要的输出:

$ find . [----options here----] | [----maybe grep or sed----] | xargs -0 -n 1 ls
./barney/src/bam bam.cpp
./barney/inc/bam bam.h
./fred/src/dino.cpp
./fred/inc/dino.h

...我想避免使用脚本和临时文件,我想这可能是我唯一的选择。

提前致谢! -马克

【问题讨论】:

  • event not found 是因为!bash 解释为历史扩展请求。单引号它出现的字符串,或者给它一个额外的转义。我推荐单引号!

标签: regex linux bash grep


【解决方案1】:

这对我有用:

find . -regextype posix-egrep -regex '.+\.(c|cpp|h)$' -not -path '*/generated/*' \
       -not -path '*/deploy/*' -print0 | xargs -0 ls -L1d

您的版本的更改很小:我单独添加了某些路径模式的排除项,因为这样更容易,并且我单引号将它们隐藏在 shell 插值中。

找不到事件是因为!bash 解释为历史扩展请求。解决方法是使用单引号而不是双引号。

小测验:sh 中的单引号字符串中有哪些特殊字符?

答案: ' 是特殊的(它结束了字符串)。这才是终极安全。

grep-Z(有时称为--null)使grep 输出 以空字符而不是换行符结束。您想要的是-z(有时称为--null-data),它会导致grep 将其输入 中的空字符解释为行尾而不是换行符。这使它在find ... -print0 的输出中按预期工作,它在每个文件名后添加一个空字符而不是换行符。

如果你是这样做的:

find . -regextype posix-egrep -regex '.+\.(c|cpp|h)$' -print0 | \
    grep -vzZ generated | grep -vzZ deploy | xargs -0 ls -1Ld

然后grep 的输入 输出将是空分隔的,它会正常工作...直到您的源文件之一开始被命名为 deployment.cpp 并开始得到“神秘地”被您的脚本排除在外。

顺便说一下,这里有一个更好的方法来生成您的测试用例文件集。

while read -r file ; do
    mkdir -p "${file%/*}"
    touch "$file"
done <<'DATA'
./barney/generated/bam bam.h
./barney/src/bam bam.cpp
./barney/deploy/bam bam.h
./barney/inc/bam bam.h
./fred/generated/dino.h
./fred/src/dino.cpp
./fred/deploy/dino.h
./fred/inc/dino.h
DATA

既然我这样做是为了验证,我想我会分享它并让你免于重复。什么都不要做两次!这就是计算机的用途。

【讨论】:

    【解决方案2】:

    你的命令:

    find . -regextype posix-egrep -regex "(?!.*(generated|deploy).*$)(.+\.(c|cpp|h)$)" -print0 | xargs -0 -n 1 ls
    

    失败,因为您尝试使用 Posix extended regular expressions,它不支持环视/后视等。https://superuser.com/a/596499/658319

    find 确实支持pcre,所以如果你转换为pcre,这应该可以工作。

    【讨论】:

      猜你喜欢
      • 2016-07-14
      • 2018-03-08
      • 2023-04-02
      • 2011-05-11
      • 1970-01-01
      • 1970-01-01
      • 2016-03-08
      • 2017-08-09
      • 2011-05-14
      相关资源
      最近更新 更多