如何打印包含两个匹配两种不同模式的文件的目录?

时间:2015-10-13 17:29:05

标签: bash unix find

我想编写一个bash脚本,其中包括Makefile,其中Makefile与python文件位于同一目录中(文件看起来像* .py)。

通过执行两个单独的查找和比较输出,我可以在几个步骤中做得相当不优雅,但我想可能有办法做一行查找命令?看起来应该有办法吗?

所以

path1/Makefile
path1/Some.py
path2/Makefile
path3/Makefile
path3/path4/Makefile
path3/path4/Another.py

将打印path1。

path2不会。

不会打印path3。 b / c python文件需要处于同一级别。

将打印path3 / path4。

所以一般的问题是,我可以使用find来查找包含至少两个文件的目录,一个匹配一个模式,另一个匹配另一个模式,但这两个模式必须由不同的文件来满足吗?

感谢。

不过,我正在使用     find(GNU findutils)4.4.2。

但是,我只是对人们提出的答案感兴趣。我已经完成了不太优雅的解决方案,但很高兴看到。它始终是有益的和有教育意义的。

5 个答案:

答案 0 :(得分:4)

我无法想办法单独查找条件,但当然你总是可以使用-exec来构造这样的东西:

find . -name '*.py' -exec bash -c 'test -f $(dirname "$1")/Makefile' -- {} \; -print

这将打印在同一目录中具有Makefile的* .py文件列表。如果在最后的正斜杠之后剥离所有内容,则只获得包含这些文件的目录,例如通过管道sed 's:/[^/]*$::'

此解决方案的优点是只运行一个find,代价是为找到的每个.py文件生成一个shell。请注意,test是大多数shell中内置的。

你可以单独在bash中完成此操作,完全绕过find

shopt -s globstar
for file in **/./*.py; do
  test "${file%/*}/Makefile" && echo "${file%/*}"
done | uniq

“globstar”选项允许**/*.py搜索子目录,如find所做的那样,使用此解决方案,我们可以使用参数扩展代替运行dirname的子shell。根据您的要求,输出是包含符合条件的文件的目录列表。如果单个目录中存在多个uniq匹配项,则输出将通过*.py进行过滤。

根据Etan的评论更新:

请注意,为了适应包含* .py和Makefile的当前(最顶层)目录的可能性,glob是**/./*.py而不是**/*.py。结果导致每个匹配的路径以点结束。虽然这仍然会找到所有目标目录(/foo/bar/./foo/bar相同),如果它困扰您,您可以通过在{{1之后添加| sed 's:/\.::'来删除尾随点路径段}}。添加这样的过滤器会使输出看起来与基于uniq的解决方案相同。

答案 1 :(得分:2)

尝试使用bash:

shopt -s globstar nullglob
for i in **/Makefile; do i="${i%/*}"; x=( "$i"/*.py ); [[ -n ${x[0]} ]] && echo "$i"; done

输出:

path1
path3/path4

答案 2 :(得分:1)

假设您只想打印出直接公共父目录而不是匹配的文件名,那么我认为这样可行。 (虽然我觉得必须有更好的方法来做到这一点。)

注意:GNU find需要-printf

find "$topdir" -name '*.py' -printf '%h\0' | xargs -0 -I {} find {} -mindepth 1 -maxdepth 1 -name Makefile -printf '%h\n' | sort -u

对于非GNU find选项(从ghoti's handiwork修改(未经测试):

find . -name '*.py' -exec sh -c 'for file; do d=$(dirname "$file"); test -f "$d"/Makefile && printf "%s\n" "$d"; done' -- {} \+

-exec ;为每个匹配的文件运行一次命令。 -exec +汇总一个命令行,其中包含适合单个命令的文件,因此对于大型匹配文件列表,运行许多更少的shell实例。

答案 3 :(得分:1)

我建议使用进程替换while read循环中使用shopt -s nullglob while IFS= read -rd '' dir; do ary=("$dir"/*.py) [[ -f "$dir"/Makefile && ${#ary[@]} -gt 0 ]] && echo "$dir" done < <(find . -type d -print0)

find

while命令将查找当前目录中的所有目录,并在*.py循环内部检查是否存在Makefile个文件和service_category_selection那些目录。

答案 4 :(得分:-1)

find有一个-o标志,可以组合多种模式。像这样:

find \( [pattern for path1] -o [pattern for path2] \)
相关问题