如何从Linux“find”命令的输出中排除与某些模式匹配的目录?

时间:2012-07-12 16:18:26

标签: regex linux bash grep

我想在Linux的find命令中使用正则表达式,以递归方式跳入庞大的目录树,向我显示所有.c,.cpp和.h文件,但省略包含某些子字符串的匹配项。最后,我想将输出发送到xargs命令,以对所有匹配文件进行某些处理。我可以通过grep管道find输出以删除包含这些子字符串的匹配项,但该解决方案对包含空格的文件名不能很好地工作。所以我尝试使用find的-print0选项,它使用nul char而不是换行符(空格)终止每个文件名,并使用xargs -0来预期以空格分隔的输入而不是空格分隔的输入,但我无法弄清楚如何成功地通过管道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文件,但不包括那些出现在'generated'和'deploy'目录中的文件。

顺便说一句,你可以创建一个完整的测试目录(名为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 args将其加强到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

...这很棒,除了我不希望输出中的'generated'和'deploy'目录。所以我试试这个:

$ 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并不喜欢(!。*:找不到事件,无论这意味着什么),即使这不是问题,我的正则表达式似乎不适用于正则表达式测试人员网站我通常使用的页面。

任何想法如何让我的工作?这是我想要的输出:

$ 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

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

提前致谢! -Mark

2 个答案:

答案 0 :(得分:5)

这对我有用:

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

因为无论如何我做了这个以确认我认为我会分享并避免重复。不要做两次!这就是计算机的用途。

答案 1 :(得分:0)

你的命令:

other_1.1.0

失败是因为您尝试使用find . -regextype posix-egrep -regex "(?!.*(generated|deploy).*$)(.+\.(c|cpp|h)$)" -print0 | xargs -0 -n 1 ls ,它不支持环视/后视等。https://superuser.com/a/596499/658319

Posix extended regular expressions支持find,因此如果您转换为pcre,这应该有效。