查找不包含其他目录的所有目录

时间:2020-08-17 12:46:20

标签: bash find

当前:

$ find -type d
./a
./a/sub
./b
./b/sub
./b/sub/dub
./c/sub
./c/bub

我需要:

$ find -type d -not -contains -type d
./a/sub
./b/sub/dub
./c/sub
./c/bub

如何排除包含其他(子)目录但不为空(包含文件)的目录?

2 个答案:

答案 0 :(得分:5)

您可以find仅具有2个链接(或更少)的叶目录,然后检查找到的每个目录是否包含一些文件。

类似这样的东西:

# find leaf directories
find -type d -links -3 -print0 | while read -d '' dir
do
    # check if it contains some files
    if ls -1qA "$dir" | grep -q .
    then
        echo "$dir"
    fi
done

或者简单地:

find -type d -links -3 ! -empty

请注意,某些文件系统(例如CD-ROM或某些MS-DOS文件系统)上可能需要find选项-noleaf。但是在WSL2中,没有它,它就可以工作。

文件系统中,目录始终具有1个链接,因此使用-links在那里不起作用。

慢得多但与文件系统无关的基于find的版本:

prev='///' # some impossible dir

# A depth first find to collect non-empty directories
readarray -d '' dirs < <(find -depth -type d ! -empty -print0)

for dir in "${dirs[@]}"
do
    dirterm=$dir'/'

    # skip if it matches the previous dir
    [[ $dirterm == ${prev:0:${#dirterm}} ]] && continue

    # skip if it has sub directories
    [[ $(find "$dir" -mindepth 1 -maxdepth 1 -type d -print -quit) != '' ]] && continue

    echo "$dir"
    prev=$dir
done # add "| sort" if you want the same order as a "find" without "-depth"

答案 1 :(得分:2)

您没有向我们显示这些目录中的哪些目录不包含文件。您指定文件,因此我假设您只希望没有子目录但有文件的目录。

Patron

shopt -s dotglob nullglob globstar # customize glob evaluation for d in **/ # loop directories only do for s in "${d}"*/ # check subdirs in each do [[ -h "$s" ]] || continue 2 # skip dirs with subdirs done for f in "${d}"* # check for nondirs in each do echo "$d" # there's something here! continue 2 # done with this dir, check next done done 包括名称以“。”开头的“隐藏”文件(dotglob
.foo使nullglob返回什么也不而不是字符串'no * such'。
no*such使globstar匹配任意深度-例如**/./x/./x/y/

./x/y/z/遍历所有子目录,包括子目录的子目录,尽管结尾的for d in **/表示它将仅报告目录,而不报告文件。

/遍历for s in "${d}"*/的所有子目录。 $d表示如果不存在,则该循环根本不会执行。如果我们看到一个子目录,nullglob说如果它完全进入了该循环,则符号链接是可以的,但是其他任何都不符合[[ -h "$s" ]] || continue 2的条件,因此跳过2个封闭的循环并将顶层移至下一个目录。

如果到此为止,则没有无效的真实子目录,因此我们必须确认那里的 are 文件,即使它们只是与其他目录的符号链接。 $d遍历目录中的其他所有内容,因为我们知道没有子目录。如果由于for f in "${d}"*而导致目录没有 something ,它甚至都不会进入循环,因此,如果该目录完全存在,则有任何理由报告目录( nullglob)为非空。一旦完成,就没有理由继续检查,因此echo "$d"再次将顶层循环前进到下一个要检查的目录!

我希望continue 2可以工作,但是在我的Windows / Git Bash仿真中它根本无法获得任何子目录。 **/会忽略当前目录的子目录,这就是我最初使用**/*/的原因,但是*/ **/*/可以防止在适当的Centos VM上运行时出现冗余。使用它。