bash 列出linux中的所有叶子子目录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1574403/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
List all leaf subdirectories in linux
提问by amol
Is there an easy way to list only directories under a given directory in Linux? To explain better, I can do:
有没有一种简单的方法可以只列出 Linux 中给定目录下的目录?为了更好地解释,我可以这样做:
find mydir -type d
which gives:
这使:
mydir/src
mydir/src/main
mydir/bin
mydir/bin/classes
What I want instead is:
我想要的是:
mydir/src/main
mydir/bin/classes
I can do this in a bash script that loops over the lines and removes previous line if next line contains the path, but I'm wondering if there is a simpler method that does not use bash loops.
我可以在 bash 脚本中执行此操作,该脚本循环遍历行并在下一行包含路径时删除上一行,但我想知道是否有不使用 bash 循环的更简单方法。
采纳答案by Brian
find . -type d | sort | awk 'find . -type d -links 2
!~ last "/" {print last} {last=drinks
|-- coke
| |-- cherry
| `-- diet
| |-- caffeine-free
| `-- cherry
|-- juice
| `-- orange
| `-- homestyle
| `-- quart
`-- pepsi
|-- clear
`-- diet
} END {print last}'
回答by mivk
If you want only the leaf directories (directories which don't contain any sub-directory), look at this other question. The answer also explains it, but in short it is:
如果您只想要叶目录(不包含任何子目录的目录),请查看其他问题。答案也解释了它,但简而言之就是:
for dir in $(find -depth -type d); do [[ ! $prev =~ $dir ]] && echo "$dir" ; prev="$dir"; done
回答by Paused until further notice.
If you're looking for something visual, tree -dis nice.
如果您正在寻找视觉效果,那tree -d很好。
saveIFS=$IFS; IFS=$'\n'; for dir in $(find -depth -type d ); do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done; IFS=$saveIFS
回答by Paused until further notice.
I can't think of anything that will do this without a loop. So, here are some loops:
我想不出有什么可以在没有循环的情况下做到这一点。所以,这里有一些循环:
This displays the leaf directories under the current directory, regardless of their depth:
这将显示当前目录下的叶目录,无论它们的深度如何:
find -depth -type d | while read dir; do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done
This version properly handles directory names containing spaces:
此版本正确处理包含空格的目录名称:
find . -type d | sed 's:$:/:' | sort -r | while read -r dir;do [[ "${dir}" != "${prev:0:${#dir}}" ]] && echo "${dir}" && prev="${dir}”;done
Here is a version using Jefromi's suggestion:
这是使用Jefromi建议的版本:
shopt -s nullglob globstar;printf "%s\n" **/ | sort -r | while read -r dir;do [[ "${dir}" != "${prev:0:${#dir}}" ]] && echo "${dir}" && prev="${dir}";done;shopt -u nullglob globstar
回答by Nerdilicious
The solution using awkis nice, simple… and fails if the directory name contains any character that is considered special in forming regex patterns. This also presents an issue with the ~or !=tests in Bash.
使用的解决方案awk很好,很简单……如果目录名称包含任何在形成正则表达式模式时被认为是特殊的字符,则失败。这也给Bash 中的~or!=测试带来了问题。
The following seems to work for both BSD and GNU find:
以下似乎适用于 BSD 和 GNU find:
find . -type d -execdir sh -c 'test -z "$(find "{}" -mindepth 1 -type d)" && echo $PWD/{}' \;
- Change
find .to any directory you want to start the search in. - The
sedcommand adds a forward slash to each directory returned byfind. sort -rsorts the directory list in reverse alphabetical order, which has the benefit of listing the directories furthest away from a root first, which is what we want.- This list is then read in line-by-line by the
while readloop, where the-roption further protects against treating certain characters differently from others. - We then need to compare the current line against the previous one. As
we cannot use the
!=test and that intermediate directories will have a path shorter than that of the corresponding leaf directory, our test will compare the current line to the previous line truncated to the length of the current line. If that's a match, then we can discard this line as a non-leaf directory, otherwise we print this line and set it as theprevious line ready for the next iteration. Note that the strings need to be quoted in the test statement, otherwise some false positives may be produced.
- 切换
find .到您想要开始搜索的任何目录。 - 该
sed命令向find.返回的每个目录添加一个正斜杠 。 sort -r以逆字母顺序对目录列表进行排序,这样做的好处是首先列出离根最远的目录,这正是我们想要的。- 然后
while read循环逐行读取此列表,其中该-r选项进一步防止将某些字符与其他字符区别对待。 - 然后我们需要将当前行与前一行进行比较。由于我们无法使用
!=测试,并且中间目录的路径将比相应的叶目录的路径短,因此我们的测试会将当前行与截断为当前行长度的前一行进行比较。如果匹配,那么我们可以将此行作为非叶目录丢弃,否则我们打印此行并将其设置为prev准备下一次迭代的ious 行。注意测试语句中需要引用字符串,否则可能会产生一些误报。
Oh, if you don't want to use find…
哦,如果你不想使用find……
find -depth -type d |sed 'h; :b; $b; N; /^\(.*\)\/.*\n$/ { g; bb }; $ {x; b}; P; D'
回答by kenorb
Try the following one-liner (tested on Linux & OS X):
尝试以下单行(在 Linux 和 OS X 上测试):
# copy the pattern space to the hold space
h
# label for branch (goto) command
:b
# on the last line ($) goto the end of
# the script (b with no label), print and exit
$b
# append the next line to the pattern space (it now contains line1\nline2
N
# if the pattern space matches line1 with the last slash and whatever comes after
# it followed by a newline followed by a copy of the part before the last slash
# in other words line2 is different from line one with the last dir removed
# see below for the regex
/^\(.*\)\/.*\n$/ {
# Undo the effect of
# the n command by copying the hold space back to the pattern space
g
# branch to label b (so now line2 is playing the role of line1
bb
}
# If the `N' command had added the last line, print and exit
# (if this is the last line then swap the hold space and pattern space
# and goto the end (b without a label)
$ { x; b }
# The lines are different; print the first and go
# back working on the second.
# print up to the first newline of the pattern space
P
# delete up to the first newline in the pattern space, the remainder, if any,
# will become line1, go to the top of the loop
D
回答by Paused until further notice.
This is still a loop, since it uses the branch command in sed:
这仍然是一个循环,因为它使用了分支命令sed:
Based on a script in info sed(uniq work-alike).
基于info sed(uniq work-alike)中的脚本。
EditHere is the sedscript broken out with comments (copied from info sedand modified):
编辑这是sed带有注释的脚本(复制自info sed并修改):
Here is what the regex is doing:
这是正则表达式正在做的事情:
/- start a pattern^- matches the beginning of the line\(- start a capture group (back reference subexpression).*- zero or more (*) of any character (.)\)- end capture group\/- a slash (/) (escaped with\).*- zero or more of any character\n- a newline\1- a copy of the back reference (which in this case is whatever was between the beginning of the line and the last slash)$- matches the end of the line/- end the pattern
/- 开始一个模式^- 匹配行的开头\(- 启动一个捕获组(反向引用子表达式).*- 零个或多个 (*) 的任何字符 (.)\)- 结束捕获组\/- 斜线 (/)(用 转义\).*- 零个或多个任何字符\n- 换行\1- 反向引用的副本(在本例中是行首和最后一个斜杠之间的任何内容)$- 匹配行尾/- 结束模式
回答by LB40
I think you can look at all the directories and then redirect the ouput and use xargs for counting the number files for each subdirectories, when there's no subdirectory ( xargs find SUBDIR -type d | wc -l ... something like that, i cannot test right now ) you've found a leaf.
我认为您可以查看所有目录,然后重定向输出并使用 xargs 计算每个子目录的文件数,当没有子目录时( xargs find SUBDIR -type d | wc -l ...类似的东西,我不能立即测试)您找到了一片叶子。
This is still a loop though.
这仍然是一个循环。

