Як я можу здійснити пошук на ширині за допомогою `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). Ви хочете лише перелічити файли, або ви хочете використовувати інші праймери?
Жил "ТАК - перестань бути злим"

@Gilles, насправді я зрозумів, що це -bfsбуде не зовсім те, що мені потрібно ... У мене є простий скрипт, який генерує індекс для великого проекту GitLab, придатного для включення в GitLab Wiki. Це робить заголовки ієрархічно на основі імен каталогів. Це чудово працює, за винятком того, що в прикладі структури файлів вище він буде розміщений deltaпід charlieпідзаголовком, а не під головним alphaзаголовком.
Wildcard

Ще одна дивна річ, що мій findвихід в алфавітному порядку. Не маю ідеї, чому ....
Wildcard

І все-таки, я думаю, це -bfs може стати в нагоді, навіть якщо це ідеально не відповідає цьому випадку використання.
Wildcard

2
Я реалізував такий інструмент: bfs . Він ще не на 100% сумісний з функціями пошуку GNU, але він туди потрапляє.
Тавіан Барнс

Відповіді:


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, щоб включити їх.

Застереження:

  • Це підірве пам'ять, якщо файлів багато.
  • Це проходить символічні посилання на каталоги рекурсивно.

Якщо ви хочете вибрати порядок або каталоги та не-каталоги, а продуктивність не є критичною, ви можете зробити два проходи та перевірити [ -d "$file" ]кожен прохід.



1
Приємно! Ще одне майже тривіальне застереження: не вдасться обробити файл, який є одиноким файлом у каталозі, якщо файл буде буквально названий *. :)
Wildcard

@Wildcard О так, я забув це згадати. Використовуйте bash або zsh з nullglobі використовуйте (($#))як умову циклу, щоб уникнути цього красного випадку.
Жил "ТАК - перестань бути злим"

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.