嗨,我有一个很大的目录树。我想找出所有包含名称以“.ext1”结尾的文件和名称以“.ext2”结尾的文件的目录。
这怎么可能?我考虑过使用两个查找,一个用于“.ext1”,一个用于“.ext2”,但是我需要找到交集,这该怎么做?
谢谢!
答案1
这是一个相对简单的解决方案,它只运行find
一次,将其输出存储在临时文件中,然后分离并整理两个扩展的结果。
tmp=$(mktemp)
find . -name '*.ext1' -o -name '*.ext2' | sort >"$tmp"
comm -12 <(<tmp sed -n 's!/[^/]*\.ext1$!!p' | sort) \
<(<tmp sed -n 's!/[^/]*\.ext2$!!p' | sort)
rm "$tmp"
另一种方法是遍历find
目录并使用辅助程序检查模式是否匹配。请注意,在这种情况下存在性检查有效,但如果您的搜索模式不匹配自身并且是可能的文件名,则需要更复杂的东西。
find . -type d -exec sh -c '{ set "$0"/*.ext1; [ -e "$1" ]; } &&
{ set "$0"/*.ext2; [ -e "$1" ]; }' {} \;
这是一个 zsh 解决方案,其运行方式类似于最后一条命令:
echo **/*(/e\''set -- $REPLY/*.ext1(N[1]) $REPLY/*.ext2(N[1]); ((#==2))'\')
这是另一个 zsh 解决方案,它查找*.ext1
并选择仅具有以下内容的目录*.ext2
:
echo ./**/*.ext1(e\''REPLY=${REPLY:h}; set -- $REPLY/*.ext2(N); ((#))'\')
这是部分 Perl 解决方案;由于 Perl 通配符的变化,如果目录名包含空格,它将不起作用(有办法解决这个问题,但我找不到一种相当优雅的方法)。
perl -l -MFile::Find -e \
'find {no_chdir => 1,
wanted => sub {<$_/*.ext1> and <$_/*.ext2> and print}}, "."'
答案2
如果您知道每个文件的确切名称:
find start_dir -type d -exec test -e {}/file.ext1 -a -e {}/file.ext2 \; -print
如果您只知道扩展名,那么这种极其丑陋的黑客攻击就会起作用:
find start_dir -type d -execdir bash -c 'shopt -s nullglob; eval '\''test -n "$(echo '{}'/*.ext1)" -a -n "$(echo '{}'/*.ext2)"'\''' \; -print
如果您有很多目录需要搜索,它也可能会非常慢。
答案3
如果从目录树的头部运行,这似乎有效
find $(find . -name "*.ext1" -printf %h\\n) -name "*.ext2" -printf %h\\n
答案4
继续使用 的另一个答案find
,根据评论,它不适用于路径中的空格,可以xargs
一起使用 和find [...] -printf
来首先找到包含第一个文件名/扩展名的目录,然后使用该目录列表查找第二个文件名/扩展名。例如,给定一些起始目录dir
(仅为了便于阅读而分成多行):
$ find dir -name "*.ext1" -exec printf '%q\n' {} \; | \
xargs -I{} dirname {} | \
xargs -I{} printf '%q\n' {} | \
xargs -I{} find {} -name '*.ext2' -printf '%h\n'
需要注意的是,如果一个目录包含两个(或更多)与任一扩展名匹配的文件,则该目录将出现两次(或更多);如果这是一个问题,只需将其附加... | sort -u
到管道即可。
注意:xargs -I{} cmd {}
仅用于将命令粘贴到语句cmd
中的所需位置xargs
。该find
命令有一个内置-printf
选项,可以引用输出字符串(作为使用 的以空字符结尾的字符串的替代find ... -print0 | xargs -0 ...
)。该find
命令-print '%h'
打印dirname
当前参数的。