简体   繁体   English

如何以广度优先的方式递归列出某个位置的所有目录?

[英]How do I recursively list all directories at a location, breadth-first?

Breadth-first list is important, here.在这里,广度优先列表很重要。 Also, limiting the depth searched would be nice.另外,限制搜索的深度会很好。

$ find . -type d
/foo
/foo/subfoo
/foo/subfoo/subsub
/foo/subfoo/subsub/subsubsub
/bar
/bar/subbar

$ find . -type d -depth
/foo/subfoo/subsub/subsubsub
/foo/subfoo/subsub
/foo/subfoo
/foo
/bar/subbar
/bar

$ < what goes here? >
/foo
/bar
/foo/subfoo
/bar/subbar
/foo/subfoo/subsub
/foo/subfoo/subsub/subsubsub

I'd like to do this using a bash one-liner, if possible.如果可能的话,我想使用 bash 单行代码来做到这一点。 If there were a javascript-shell, I'd imagine something like如果有一个javascript-shell,我会想象像

bash("find . -type d").sort( function (x) x.findall(/\//g).length; )

The find command supports -printf option which recognizes a lot of placeholders. find命令支持-printf选项,它可以识别很多占位符。

One such placeholder is %d which renders the depth of given path, relative to where find started.一个这样的占位符是%d ,它呈现给定路径的深度,相对于find开始的位置。

Therefore you can use following simple one-liner:因此,您可以使用以下简单的一行代码:

find -type d -printf '%d\t%P\n' | sort -r -nk1 | cut -f2-

It is quite straightforward, and does not depend on heavy tooling like perl .它非常简单,不依赖于像perl这样的重型工具。

How it works:怎么运行的:

  • it internally generates list of files, each rendered as a two-field line它在内部生成文件列表,每个文件呈现为两行
  • the first field contains the depth, which is used for (reverse) numerical sorting, and then cut away第一个字段包含深度,用于(反向)数值排序,然后切掉
  • resulting is simple file listing, one file per line, in the deepest-first order结果是简单的文件列表,每行一个文件,按最深的优先顺序

If you want to do it using standard tools, the following pipeline should work:如果你想使用标准工具来完成它,下面的管道应该可以工作:

find . -type d | perl -lne 'print tr:/::, " $_"' | sort -n | cut -d' ' -f2

That is,那是,

  1. find and print all the directories here in depth first order查找并打印这里的所有目录
  2. count the number of slashes in each directory and prepend it to the path计算每个目录中斜杠的数量并将其添加到路径中
  3. sort by depth (ie, number of slashes)按深度排序(即斜杠数)
  4. extract just the path.只提取路径。

To limit the depth found, add the -maxdepth argument to the find command.要限制找到的深度,请将 -maxdepth 参数添加到查找命令。

If you want the directories listed in the same order that find output them, use "sort -n -s" instead of "sort -n";如果您希望目录以与 find 输出它们相同的顺序列出,请使用“sort -n -s”而不是“sort -n”; the "-s" flag stabilizes the sort (ie, preserves input order among items that compare equally). “-s”标志稳定排序(即,保留比较相等的项目之间的输入顺序)。

You can use find command, find /path/to/dir -type d So below example list of directories in current directory:您可以使用 find 命令,find /path/to/dir -type d 下面是当前目录中目录的示例列表:

find . -type d

My feeling is that this is a better solution than previously mentioned ones.我的感觉是,这是比前面提到的更好的解决方案。 It involves grep and such and a loop, but I find it works very well, specifically for cases where you want things line buffered and not the full find buffered.它涉及 grep 和循环,但我发现它工作得很好,特别是对于你想要缓冲行而不是缓冲完整查找的情况。

It is more resource intensive because of:它需要更多的资源,因为:

  • Lots of forking很多分叉
  • Lots of finds很多发现
  • Each directory before the current depth is hit by find as many times as there is total depth to the file structure (this shouldn't be a problem if you have practically any amount of ram...)当前深度之前的每个目录被 find 命中的次数与文件结构的总深度一样多(如果您实际上有任何数量的 ram,这应该不是问题......)

This is good because:这很好,因为:

  • It uses bash and basic gnu tools它使用 bash 和基本的 gnu 工具
  • It can be broken whenever you want (like you see what you were looking for fly by)它可以随时被打破(就像你看到你正在寻找的东西飞过一样)
  • It works per line and not per find, so subsequent commands don't have to wait for a find and a sort它按行工作而不是按查找工作,因此后续命令不必等待查找和排序
  • It works based on the actual file system separation, so if you have a directory with a slash in it, it won't be listed deeper than it is;它基于实际的文件系统分隔工作,所以如果你有一个目录中有一个斜线,它不会被列得比它更深; if you have a different path separator configured, you still are fine.如果您配置了不同的路径分隔符,您仍然可以。
#!/bin/bash 
depth=0

while find -mindepth $depth -maxdepth $depth | grep '.'
do
    depth=$((depth + 1))
done

You can also fit it onto one line fairly(?) easily:你也可以很容易地将它放在一条线上(?):

depth=0; while find -mindepth $depth -maxdepth $depth | grep --color=never '.'; do depth=$((depth + 1)); done

But I prefer small scripts over typing...但我更喜欢小脚本而不是打字......

I don't think you could do it using built-in utilities, since when traversing a directory hierarchy you almost always want a depth-first search, either top-down or bottom-up.我认为您不能使用内置实用程序来完成此操作,因为在遍历目录层次结构时,您几乎总是希望进行深度优先搜索,无论是自上而下还是自下而上。 Here's a Python script that will give you a breadth-first search:这是一个 Python 脚本,可以为您提供广度优先搜索:

import os, sys

rootdir = sys.argv[1]
queue = [rootdir]

while queue:
    file = queue.pop(0)
    print(file)
    if os.path.isdir(file):
        queue.extend(os.path.join(file,x) for x in os.listdir(file))

Edit:编辑:

  1. Using os.path -module instead of os.stat -function and stat -module.使用os.path -module 而不是os.statstat -module。
  2. Using list.pop and list.extend instead of del and += operators.使用list.poplist.extend代替del+=运算符。

I tried to find a way to do this with find but it doesn't appear to have anything like a -breadth option.我试图找到一种使用find来执行此操作的方法,但它似乎没有-breadth选项之类的东西。 Short of writing a patch for it, try the following shell incantation (for bash):如果没有为它编写补丁,请尝试以下 shell 咒语(用于 bash):

LIST="$(find . -mindepth 1 -maxdepth 1 -type d)";
while test -n "$LIST"; do
    for F in $LIST; do
        echo $F;
        test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)";
    done;
    LIST=$NLIST;
    NLIST="";
done

I sort of stumbled upon this accidentally so I don't know if it works in general (I was testing it only on the specific directory structure you were asking about)我有点偶然发现了这个,所以我不知道它是否一般有效(我只在你询问的特定目录结构上测试它)

If you want to limit the depth, put a counter variable in the outer loop, like so (I'm also adding comments to this one):如果你想限制深度,在外循环中放置一个计数器变量,就像这样(我也在为这个添加注释):

# initialize the list of subdirectories being processed
LIST="$(find . -mindepth 1 -maxdepth 1 -type d)";
# initialize the depth counter to 0
let i=0;
# as long as there are more subdirectories to process and we haven't hit the max depth
while test "$i" -lt 2 -a -n "$LIST"; do
    # increment the depth counter
    let i++;
    # for each subdirectory in the current list
    for F in $LIST; do
        # print it
        echo $F;
        # double-check that it is indeed a directory, and if so
        # append its contents to the list for the next level
        test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)";
    done;
    # set the current list equal to the next level's list
    LIST=$NLIST;
    # clear the next level's list
    NLIST="";
done

(replace the 2 in -lt 2 with the depth) (将-lt 2中的 2 替换为深度)

Basically this implements the standard breadth-first search algorithm using $LIST and $NLIST as a queue of directory names.基本上,这实现了标准的广度优先搜索算法,使用$LIST$NLIST作为目录名称队列。 Here's the latter approach as a one-liner for easy copy-and-paste:这是后一种方法,作为易于复制和粘贴的单行方法:

LIST="$(find . -mindepth 1 -maxdepth 1 -type d)"; let i=0; while test "$i" -lt 2 -a -n "$LIST"; do let i++; for F in $LIST; do echo $F; test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)"; done; LIST=$NLIST; NLIST=""; done

Without the deserved ordering: find -maxdepth -type d没有应有的顺序:find -maxdepth -type d

To get the deserved ordering, you have to do the recursion yourself, with this small shellscript:要获得应有的顺序,您必须使用这个小的 shellscript 自己进行递归:

#!/bin/bash
r () 
{
    let level=$3+1
    if [ $level -gt $4 ]; then return 0; fi
    cd "$1"
    for d in *; do
        if [ -d "$d" ]; then
            echo $2/$d
        fi;
    done
    for d in *; do
        if [ -d "$d" ]; then
            (r "$d" "$2/$d" $level $4)
        fi;
    done
}
r "$1" "$1" 0 "$2"

Then you can call this script with parameters base directory and depth.然后你可以用参数基目录和深度调用这个脚本。

Here's a possible way, using find.这是一种可能的方法,使用 find。 I've not thoroughly tested it, so user beware...我还没有彻底测试它,所以用户要小心......

depth=0
output=$(find . -mindepth $depth -maxdepth $depth -type d | sort); 
until [[ ${#output} -eq 0 ]]; do 
  echo "$output"
  let depth=$depth+1
  output=$(find . -mindepth $depth -maxdepth $depth -type d | sort)
done

Something like this:像这样:

find . -type d | 
  perl -lne'push @_, $_;
    print join $/,
      sort { 
        length $a <=> length $b || 
          $a cmp $b 
        } @_ if eof'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM