如何使用`find`进行广度优先搜索?


17

-depth伯〜find导致它执行深度优先搜索。

但是,默认序列不是广度优先搜索。

默认序列可以非正式地描述为“深度优先遍历,它在第一次遇到节点时进行处理,而不是在回溯过程中进行处理。”

我实际需要广度优先搜索。如何使find这种行为?


为了说明,请使用以下设置:

$ mkdir -p alpha/{bravo,charlie,delta}
$ touch alpha/charlie/{alpha,beta,gamma,phi}

find 具有以下默认行为:

$ find alpha
alpha
alpha/charlie
alpha/charlie/alpha
alpha/charlie/phi
alpha/charlie/beta
alpha/charlie/gamma
alpha/delta
alpha/bravo

并使用-depth,它的执行如下:

$ find alpha -depth
alpha/charlie/alpha
alpha/charlie/phi
alpha/charlie/beta
alpha/charlie/gamma
alpha/charlie
alpha/delta
alpha/bravo
alpha

但是,我想要以下(虚拟)选项:

$ find alpha -bfs
alpha
alpha/charlie
alpha/delta
alpha/bravo
alpha/charlie/alpha
alpha/charlie/phi
alpha/charlie/beta
alpha/charlie/gamma

换句话说,我需要在给定深度下find处理/报告所有文件/目录,然后再继续。

我怎样才能做到这一点?


不与find(至少,不仅与find)在一起。您是否只想列出文件,还是要使用其他主文件?
吉尔斯(Gillles)“所以-别再作恶了”

@Gilles,实际上我意识到那-bfs不是我所需要的...我有一个简单的脚本,可以为大型GitLab项目生成索引,适合包含在GitLab Wiki中。它根据目录名称分层构造标题。它的伟大工程,除了在示例文件结构上面会放delta的下charlie的子报头,而不是依据母alpha头。
通配符

另一个奇怪的是,我的find输出按字母顺序排序的。不知道为什么....
通配符

尽管如此,我认为这-bfs 可能会派上用场,即使它不完全适合此用例。
通配符

2
我实现了这样一个工具:bfs。目前还不是100%与GNU find兼容的功能,但是已经实现了。
塔维安·巴恩斯

Answers:


6

您可以仅使用shell通配符即可。用逐渐增加的目录级别建立模式。

pattern='*'
set -- $pattern
while [ $# -ne 1 ] || [ "$1" != "$pattern" ]; do
  for file; do
    …
  done
  pattern="$pattern/*"
  set -- $pattern
done

这会丢失点文件。使用FIGNORE='.?(.)'在ksh中,shopt -s dotglob在bash,或者setopt glob_dots在zsh来包括它们。

注意事项:

  • 如果有很多文件,这将耗尽内存。
  • 这会遍历遍历目录的符号链接。

如果要选择顺序或目录和非目录,并且性能并不重要,则可以进行两次通过,然后[ -d "$file" ]对每次通过进行测试。


@Wildcard是的,我做到了。
吉尔斯(Gilles)'所以

1
真好!另一个几乎不重要的警告:如果文件名是,它将无法处理目录中的唯一文件*。:)
通配符

@Wildcard哦,是的,我忘了提了。将bash或zsh与一起nullglob使用,并(($#))用作循环条件以避免这种极端情况。
吉尔(Gilles)“所以,别再邪恶了”

5

# cat ./bfind

#!/bin/bash
i=0
while results=$(find "$@" -mindepth $i -maxdepth $i) && [[ -n $results ]]; do
  echo "$results"
  ((i++))
done

这可以通过增加find和重复的深度来实现,我认为它可以重复结果,但是可以轻松过滤


抱歉,我不了解格式化机制。无论如何,实际上它不会重复,我认为是因为它切断了除mindepth之外的任何内容
user239175

3

您可以通过管道将find其排序,该排序主要按/路径名中的字符数排序。例如,

find alpha |
awk '{n=gsub("/","/",$0);printf "%04d/%s\n",n,$0}' |
sort -t/ |
sed 's|[^/]*/||'

这用于awk在路径名前加上斜杠数,并sed在末尾删除该前缀。

实际上,由于您可能希望在目录alpha/charlie+后列出目录的内容alpha/charlie,因此您需要说出sort -t/ -k1,1 -k2,2 -k3,3 -k4,4所需的深度。


0

另一个不是基于“查找”而是基于bash的答案-首先使用“父目录的长度”,然后按字母排序。

答案并不完全匹配,因为您的结果具有“ charlie,bravo,delta”,但我想知道它是否应按字母顺序排列为“ bravo,charlie,delta”。

paths_breadth_first() {
  while IFS= read -r line; do
    dirn=${line%/*}         ## dirname(line)
    echo ${#dirn},$line     ## len(dirn),line
  done | sort -n | cut -d ',' -f 2-
}

那产生

  $ cat /tmp/yy | paths_breadth_first 
  alpha
  alpha/bravo
  alpha/charlie
  alpha/delta
  alpha/charlie/alpha
  alpha/charlie/beta
  alpha/charlie/gamma
  alpha/charlie/phi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.