Bagaimana saya bisa melakukan pencarian pertama dengan menggunakan `find`?


16

Yang -depthutama findmenyebabkannya melakukan pencarian kedalaman-pertama.

Namun, urutan default bukan pencarian pertama.

Urutan default dapat digambarkan secara informal sebagai "traversal kedalaman-pertama yang menangani node ketika mereka pertama kali ditemui daripada melakukannya selama backtracking."

Saya memiliki kebutuhan aktual untuk pencarian pertama yang luas. Bagaimana saya bisa findbersikap seperti ini?


Sebagai ilustrasi, dengan pengaturan berikut:

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

find memiliki perilaku default berikut:

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

dan dengan -depth, ini berfungsi sebagai berikut:

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

Namun, yang saya inginkan adalah opsi (fiktif) berikut:

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

Dengan kata lain saya perlu findmemproses / melaporkan semua file / dir pada kedalaman yang diberikan sebelum melanjutkan.

Bagaimana saya bisa melakukan ini?


Tidak dengan find(setidaknya, tidak hanya dengan find). Apakah Anda hanya ingin membuat daftar file, atau Anda ingin menggunakan pemilihan pendahuluan lainnya?
Gilles 'SO- stop being evil'

@Gilles, sebenarnya saya menyadari bahwa -bfstidak akan seperti yang saya butuhkan ... Saya punya skrip sederhana yang menghasilkan indeks ke proyek GitLab besar, cocok untuk dimasukkan dalam GitLab Wiki. Itu membuat header secara hierarkis berdasarkan nama direktori. Ini berfungsi dengan baik, kecuali bahwa dalam struktur file contoh di atas itu akan diletakkan di deltabawah charliesubjudul, bukan di bawah alphatajuk induk .
Wildcard

Hal lain yang aneh adalah bahwa saya findkeluaran yang diurutkan berdasarkan abjad. Tidak tahu mengapa ....
Wildcard

Namun, saya pikir -bfs bisa berguna, bahkan jika itu tidak cocok dengan kasus penggunaan ini.
Wildcard

2
Saya menerapkan alat seperti itu: bfs . Ini belum 100% kompatibel dengan fitur GNU, tetapi sudah ada di sana.
Tavian Barnes

Jawaban:


6

Anda dapat melakukannya hanya dengan wildcard. Membangun pola dengan tingkat direktori yang semakin meningkat.

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

Ini melewatkan file dot. Gunakan FIGNORE='.?(.)'di ksh, shopt -s dotglobdi bash, atau setopt glob_dotsdi zsh untuk memasukkannya.

Peringatan:

  • Ini akan meledakkan memori jika ada banyak file.
  • Ini melintasi tautan simbolik ke direktori secara rekursif.

Jika Anda ingin memilih urutan atau direktori dan non-direktori, dan kinerja tidak kritis, Anda dapat membuat dua lintasan dan menguji [ -d "$file" ]setiap lintasan.


@ Kartu Memori Ya, saya lakukan.
Gilles 'SO- stop being evil'

1
Bagus! Satu lagi peringatan hampir sepele: Ini akan gagal untuk memproses file yang merupakan file tunggal dalam direktori jika file tersebut dinamai secara harfiah *. :)
Wildcard

@ Kartu Memori Oh, ya, saya lupa menyebutkan itu. Gunakan bash atau zsh dengan nullglobdan gunakan (($#))sebagai kondisi loop untuk menghindari kasus tepi ini.
Gilles 'SANGAT berhenti menjadi jahat'

5

# cat ./bfind

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

Ini bekerja dengan meningkatkan kedalaman finddan mengulangi, saya pikir ini mungkin mengulangi hasil, tetapi bisa disaring dengan mudah


Maaf saya tidak tahu tentang mekanisme pemformatan. Pokoknya, sebenarnya itu tidak mengulangi saya pikir karena itu memotong sesuatu yang kurang dari mindepth
user239175

3

Anda bisa menyalurkannya findke semacam yang terutama mengurutkan berdasarkan jumlah /karakter di pathname. Sebagai contoh,

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

Ini digunakan awkuntuk awalan pathname dengan jumlah garis miring, dan seduntuk menghapus awalan ini di akhir.

Sebenarnya, karena Anda mungkin ingin isi direktori alpha/charlie+terdaftar setelahnya alpha/charlie, Anda perlu mengatakan sort -t/ -k1,1 -k2,2 -k3,3 -k4,4hingga kedalaman yang diinginkan.


0

Jawaban lain tidak berdasarkan 'find' tetapi pada bash - gunakan "length of parent directory" terlebih dahulu, lalu urutkan berdasarkan alpha.

Jawabannya tidak cocok dengan hasil Anda memiliki "charlie, bravo, delta" tetapi saya bertanya-tanya apakah itu harus "bravo, charlie, delta" dalam urutan alfa.

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-
}

Itu menghasilkan

  $ 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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.