如何才能列出没有其他子目录的目录?
/A /A/AA /A/AB /A/AB/ABB /B /C /C/CC /C/CC/CCC /C/CC/CCC/CCCC
想象一个我想用来find
列出的结构/A/AA /A/AB/ABB /B /C/CC/CCC/CCCC
。
起点应该是find . -type d
,但都-mindepth
不能-maxdepth
使用,能有-noleaf
帮助吗(我无法让它按照我想要的方式做出反应)?
答案1
这是一个符合 POSIX 的解决方案,它对输出进行后处理find
以删除具有列出的子目录的目录。它假定目录名称中没有换行符。
{ find . -type d; echo; } |
awk 'index($0,prev"/")!=1 && NR!=1 {print prev}
1 {sub(/\/$/,""); prev=$0}'
解释:awk 脚本会延迟打印每一行,直到读取下一行为止,并且仅在前一行不是前缀时才打印前一行。这利用了find
列出父级目录后立即列出子目录的事实。额外的"/"
是为了避免错误地删除foo
也foobar
存在的目录。不雅之处NR!=1
是避免打印初始空行,不雅之echo;
处是最后一行没有同样不雅的特殊情况。调用会sub
从顶层目录中删除尾部斜杠,以防find ./
调用 eg。
和平常一样,有一个神秘的 zsh 单行命令。
echo **/.(e\''test -z $REPLY/*(/DN[1])'\':h)
更长、更易读的版本:
is_leaf () { [ -z $REPLY/*(/DN[1]) ] }
echo **/.(+is_leaf:h)
echo **/(+is_leaf)
如果您不介意结尾的,最后一行可以简化为/
。
摘要解释:括号里的内容是glob 限定符,在zshexpn
手册页中有记录。我们过滤 glob 的结果**/
(扩展到当前目录及其所有子目录),仅保留函数is_leaf
(或 之间的代码'…'
)返回 0 的结果。过滤器代码将匹配项测试的子目录($REPLY
)全部匹配(实际上,[1]
使其在第一个子目录后停止)并返回一个状态,指示是否至少找到一个子目录。 glob 限定符/
将扩展限制为目录;N
表示如果没有匹配项,则扩展为空;D
导致包含点文件;:h
是历史修饰符并导致/.
后缀被剥离(一般来说,它意味着dirname
)。
为了说明 zsh 的 glob 限定符的可能性,这里还有另外两个变体(更长而且我认为更晦涩)及其相应的is_leaf
功能:
echo **/.(e\''tmp=($REPLY/*(/DN[1])); ((!#tmp))'\':h)
echo **/.(e\''$REPLY/*(/DN[1]e:REPLY=false:)'\':h)
is_leaf () { set -- $REPLY/*(/DN[1]); ((!#)); }
is_leaf () { return $REPLY/*(/DN[1]e:REPLY=1:) }
答案2
这是我使用的:
leaf () { find "${1:-.}" -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'; }
使用起始目录来调用它:
leaf /start/dir
答案3
查找没有子目录的目录:
find dir -type d -links 2
解释:一个目录包含指向其每个子目录的链接、来自其父目录的链接和指向其自身的链接,因此,如果没有子目录,则链接数为 2。