当前:
$ 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
如何排除包含其他(子)目录但不为空(包含文件)的目录?
答案 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中,没有它,它就可以工作。
在btrfs文件系统中,目录始终具有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上运行时出现冗余。使用它。