Come posso fare una prima ricerca usando "find"?


16

Il -depthprincipale a findcausare una ricerca approfondita prima.

Tuttavia, la sequenza predefinita non è una ricerca per ampiezza.

La sequenza predefinita potrebbe essere descritta in modo informale come "un attraversamento in profondità che gestisce i nodi quando vengono incontrati per la prima volta anziché farlo durante il backtracking".

Ho un reale bisogno di ampie ricerche prima. Come posso findcomportarmi in questo modo?


A titolo illustrativo, con la seguente configurazione:

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

find ha il seguente comportamento predefinito:

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

e con -depth, si comporta come segue:

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

Tuttavia, ciò che voglio è la seguente (fittizia) opzione:

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

In altre parole, devo findelaborare / riferire su tutti i file / directory a una determinata profondità prima di procedere oltre.

Come posso fare questo?


Non con find(almeno, non solo find). Vuoi solo elencare i file o vuoi usare altri primari?
Gilles 'SO- smetti di essere malvagio' il

@Gilles, in realtà mi sono reso conto che -bfsnon sarebbe proprio quello di cui ho bisogno ... Ho un semplice script che genera un indice per un grande progetto GitLab, adatto per l'inclusione nel Wiki GitLab. Rende le intestazioni gerarchicamente basate sui nomi di directory. Funziona benissimo, tranne per il fatto che nella struttura di file di esempio sopra sarebbe messa deltasotto il charliesottotitolo, anziché sotto l' alphaintestazione principale .
Wildcard il

Un'altra cosa strana è che il mio findoutput è in ordine alfabetico. Non ho idea del perché ....
Wildcard il

Tuttavia, penso che -bfs potrebbe tornare utile, anche se non si adatta perfettamente a questo caso d'uso.
Wildcard il

2
Ho implementato un tale strumento: bfs . Non è ancora al 100% compatibile con le funzionalità di GNU, ma ci sta arrivando.
Tavian Barnes,

Risposte:


6

Puoi farlo con solo i caratteri jolly della shell. Costruire un modello con progressivamente più livelli di directory.

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

Questo manca i file dot. Usa FIGNORE='.?(.)'in ksh, shopt -s dotglobin bash o setopt glob_dotsin zsh per includerli.

Avvertenze:

  • Questo farà esplodere la memoria se ci sono molti file.
  • Questo attraversa ricorsivamente i collegamenti simbolici alle directory.

Se si desidera scegliere l'ordine o le directory e le non directory e le prestazioni non sono fondamentali, è possibile effettuare due passaggi e testare [ -d "$file" ]su ogni passaggio.


@Wildcard Sì, l'ho fatto.
Gilles 'SO- smetti di essere malvagio' il

1
Bello! Un altro avvertimento quasi banale: non riuscirà a elaborare un file che è il file solitario in una directory se il file viene letteralmente nominato *. :)
Wildcard il

@Wildcard Oh, sì, ho dimenticato di menzionarlo. Usa bash o zsh con nullglobe usa (($#))come condizione del loop per evitare questo caso limite.
Gilles 'SO- smetti di essere malvagio' il

5

# cat ./bfind

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

Questo funziona aumentando la profondità finde ripetendo, penso che potrebbe ripetere i risultati, ma potrebbe essere filtrato facilmente


Spiacente, non sapevo del meccanismo di formattazione. Ad ogni modo, in realtà non si ripete, penso perché interrompe qualcosa di meno che mindepth
user239175

3

Puoi convogliare il tuo findin un ordinamento che ordina principalmente per il numero di /caratteri nel percorso. Per esempio,

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

Questo utilizza awkper aggiungere il prefisso al percorso con il numero di barre, e sedper rimuovere questo prefisso alla fine.

In realtà, poiché probabilmente si desidera che il contenuto della directory alpha/charlie+venga elencato dopo alpha/charlie, è necessario dire sort -t/ -k1,1 -k2,2 -k3,3 -k4,4fino alla profondità desiderata.


0

Un'altra risposta non basata su 'trova' ma su bash - usa prima la "lunghezza della directory principale", quindi ordina per alpha.

La risposta non corrisponde esattamente perché i tuoi risultati hanno "charlie, bravo, delta" ma mi chiedevo se dovesse essere "bravo, charlie, delta" in ordine 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-
}

Questo produce

  $ 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.