`find`を使用して幅優先検索を実行するにはどうすればよいですか?


16

-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)。ファイルのみを一覧表示しますか、それとも他のプライマリを使用しますか?
Gilles「SO-邪悪なことをやめよ」

@ギレス、実際には-bfs私が必要なものではないことに気付きました... GitLab Wikiに含めるのに適した、大規模なGitLabプロジェクトへのインデックスを生成する簡単なスクリプトがあります。ディレクトリ名に基づいて階層的にヘッダーを作成します。上の例のファイル構造では、親ヘッダーの下ではなくdeltacharlieサブalphaヘッダーの下に置かれることを除いて、それはうまく機能します。
ワイルドカード

別の奇妙なことは、私のfind出力アルファベット順にソートされていることです。理由はわかりません....
ワイルドカード

それでも、私は考える-bfs ことができ、それは完全にこのユースケースに適合しない場合でも、便利になります。
ワイルドカード

2
私はそのようなツールを実装しました:bfs。それはまだGNU findと100%機能互換ではありませんが、それはそこにあります。
Tavian Barnes

回答:


6

シェルのワイルドカードだけでそれを行うことができます。徐々に多くのディレクトリレベルでパターンを構築します。

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

これはドットファイルを見逃します。それらを含めるには、FIGNORE='.?(.)'ksh、shopt -s dotglobbash、またはsetopt glob_dotszsh で使用します。

警告:

  • 多くのファイルがある場合、これはメモリを爆破します。
  • これは、ディレクトリへのシンボリックリンクを再帰的に走査します。

順序またはディレクトリと非ディレクトリを選択する必要があり、パフォーマンスが重要でない場合は、2つのパスを作成[ -d "$file" ]し、各パスでテストできます。


@ワイルドカードはい、そうしました。
Gilles「SO-邪悪なことをやめ

1
いいね!もう1つのほとんど自明な警告:ファイルが文字通り名前が付けられて*いる場合、ディレクトリ内の唯一のファイルであるファイルの処理に失敗します。:)
ワイルドカード

@ワイルドカードああ、はい、私はそれを言及するのを忘れていました。このエッジケースを回避nullglobするに(($#))は、bashまたはzshをループ条件として使用します。
ジル 'SO-邪悪なことをやめる'

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に基づく別の答え-「親ディレクトリの長さ」を最初に使用してから、アルファで並べ替えます。

結果には「チャーリー、ブラボー、デルタ」が含まれているため、答えは完全には一致しませんが、「ブラボー、チャーリー、デルタ」の順に並べるべきかどうか疑問に思いました。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.