2009-02-12 16 views
21

L'elenco di ampiezza è importante, qui. Inoltre, limitare la profondità cercata sarebbe bello.Come faccio a elencare in modo ricorsivo tutte le directory in una posizione, prima di tutto?

$ find . -type d 
/foo 
/foo/subfoo 
/foo/subfoo/subsub 
/foo/subfoo/subsub/subsubsub 
/bar 
/bar/subbar 

$ find . -type d -depth 
/foo/subfoo/subsub/subsubsub 
/foo/subfoo/subsub 
/foo/subfoo 
/foo 
/bar/subbar 
/bar 

$ < what goes here? > 
/foo 
/bar 
/foo/subfoo 
/bar/subbar 
/foo/subfoo/subsub 
/foo/subfoo/subsub/subsubsub 

Mi piacerebbe farlo utilizzando un one-liner bash, se possibile. Se ci fosse un javascript-shell, mi immagino qualcosa come

bash("find . -type d").sort(function (x) x.findall(/\//g).length;) 
+0

Potrebbe estendere a questo per includere lingua di propria scelta, e il sistema operativo –

+0

Arg (Linux?)! Questa è una domanda "wiki della comunità". Fastidioso. –

+0

cosa rende questa una domanda wiki della comunità? –

risposta

15

Il comando find supporta -printf opzione che riconosce un sacco di segnaposto.

Uno di questi segnaposto è %d che restituisce la profondità del percorso specificato, relativa a dove è stato avviato find.

Pertanto è possibile utilizzare seguente semplice-liner:

find -type d -printf '%d\t%P\n' | sort -r -nk1 | cut -f2- 

E 'molto semplice, e non dipende da utensili pesanti come perl.

Come funziona:

  • genera internamente elenco di file, ciascuna reso come una linea a due campi
  • primo campo contiene la profondità, che viene utilizzato per (reverse) ordinamento numerico, e poi tagliare via
  • risultante è semplice elenco dei file, un file per riga, nel più profondo-primo ordine
+0

Semplice ed elegante per un solo liner. Grazie! – gnzg

2

Senza l'ordine meritato: trovare -maxdepth tipo d

Per ottenere l'ordinamento meritato, quello che dovete fare la ricorsione da soli , con questo piccolo script di shell:

#!/bin/bash 
r() 
{ 
    let level=$3+1 
    if [ $level -gt $4 ]; then return 0; fi 
    cd "$1" 
    for d in *; do 
     if [ -d "$d" ]; then 
      echo $2/$d 
     fi; 
    done 
    for d in *; do 
     if [ -d "$d" ]; then 
      (r "$d" "$2/$d" $level $4) 
     fi; 
    done 
} 
r "$1" "$1" 0 "$2" 

Quindi è possibile chiamare questo script con i parametri directory di base e profondità.

+0

Questo è esattamente ciò che voglio, ma con l'ordine sbagliato. Ho cambiato la domanda per chiarire, grazie! –

+0

vedi la mia aggiunta! Non avevo finito :) – ypnos

4

Non penso che si possa fare usando le utilità incorporate, poiché quando si attraversa una gerarchia di directory si desidera quasi sempre una ricerca in profondità, dall'alto verso il basso o dal basso verso l'alto. Ecco uno script Python che vi darà una ricerca in ampiezza:

import os, sys 

rootdir = sys.argv[1] 
queue = [rootdir] 

while queue: 
    file = queue.pop(0) 
    print(file) 
    if os.path.isdir(file): 
     queue.extend(os.path.join(file,x) for x in os.listdir(file)) 

Edit:

  1. Utilizzando os.path -module invece di os.stat -funzione e stat -module.
  2. Utilizzo di list.pop e list.extend invece di del e += operatori.
3

ho cercato di trovare un modo per fare questo con find ma non sembra avere nulla come una scelta -breadth. Breve di scrivere una patch per esso, provare quanto segue shell formula magica (per bash):

LIST="$(find . -mindepth 1 -maxdepth 1 -type d)"; 
while test -n "$LIST"; do 
    for F in $LIST; do 
     echo $F; 
     test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)"; 
    done; 
    LIST=$NLIST; 
    NLIST=""; 
done 

I sorta di accidentalmente imbattuto in questo modo non so se funziona in generale (stavo testando solo su la struttura specifica directory che stavi chiedendo circa)

Se si vuole limitare la profondità, mettere una variabile contatore nel ciclo esterno, in questo modo (sto anche l'aggiunta di commenti a questo):

# initialize the list of subdirectories being processed 
LIST="$(find . -mindepth 1 -maxdepth 1 -type d)"; 
# initialize the depth counter to 0 
let i=0; 
# as long as there are more subdirectories to process and we haven't hit the max depth 
while test "$i" -lt 2 -a -n "$LIST"; do 
    # increment the depth counter 
    let i++; 
    # for each subdirectory in the current list 
    for F in $LIST; do 
     # print it 
     echo $F; 
     # double-check that it is indeed a directory, and if so 
     # append its contents to the list for the next level 
     test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)"; 
    done; 
    # set the current list equal to the next level's list 
    LIST=$NLIST; 
    # clear the next level's list 
    NLIST=""; 
done 

(sostituire il 2 in -lt 2 con la profondità)

Fondamentalmente questo implementa l'algoritmo di ricerca standard in larghezza utilizzando $LIST e $NLIST come coda di nomi di directory.Ecco il secondo approccio come un one-liner per un facile copia-e-incolla:

LIST="$(find . -mindepth 1 -maxdepth 1 -type d)"; let i=0; while test "$i" -lt 2 -a -n "$LIST"; do let i++; for F in $LIST; do echo $F; test -d "$F" && NLIST="$NLIST $(find $F -maxdepth 1 -mindepth 1 -type d)"; done; LIST=$NLIST; NLIST=""; done 
+1

Guardando di nuovo, questo sicuramente rende la mia lista di "cose ​​che non dovrebbero mai essere fatte in Bash" ;-) –

+0

Puoi anche formattarlo non come one-liner per facilitare la comprensione del codice ? (Ma sì, non farlo in bash :-) –

+0

Buona idea, ora formattato e commentato ;-) –

19

Se si vuole farlo utilizzando strumenti standard, il seguente gasdotto dovrebbe funzionare:

find . -type d | perl -lne 'print tr:/::, " $_"' | sort -n | cut -d' ' -f2 

Cioè,

  1. trovare e stampare tutte le directory qui in profondità primo ordine
  2. contare il numero di barre in ogni directory e anteporre al percorso
  3. ordinamento per profondità (cioè numero di barre)
  4. estrarre solo il percorso.

Per limitare la profondità trovata, aggiungere l'argomento -maxdepth al comando find.

Se si desidera che le directory elencate nello stesso ordine in cui vengono trovate in uscita, utilizzare "sort -n -s" anziché "sort -n"; il flag "-s" stabilizza l'ordinamento (ad esempio, conserva l'ordine di input tra gli elementi che si confrontano equamente).

+0

Aggiungere "2>/dev/null" al comando find, ad esempio find. -type d 2>/dev/null Assicurati che l'errore di ricerca non rovini i risultati. –

+0

Come ordinare alfabeticamente? – mr5

+0

Non funziona se i nomi di directory hanno spazi. Ad esempio, se una dir è "/ data/Mundial/Trinidad \ y \ Tobago /" avrai solo "/ data/arbol/Mundial/Trinidad". – alemol

1

Ecco un modo possibile, utilizzando Trova. Non ho accuratamente testato, così l'utente attenzione ...

depth=0 
output=$(find . -mindepth $depth -maxdepth $depth -type d | sort); 
until [[ ${#output} -eq 0 ]]; do 
    echo "$output" 
    let depth=$depth+1 
    output=$(find . -mindepth $depth -maxdepth $depth -type d | sort) 
done 
0

Qualcosa di simile a questo:

find . -type d | 
    perl -lne'push @_, $_; 
    print join $/, 
     sort { 
     length $a <=> length $b || 
      $a cmp $b 
     } @_ if eof' 
5

mia sensazione è che questa è una soluzione migliore di quelli menzionati in precedenza. Coinvolge grep e tale e un ciclo, ma trovo che funzioni molto bene, in particolare per i casi in cui si desidera che le linee vengano bufferizzate e non il buffer completo trovato.

E 'più alta intensità di risorse a causa di:

  • sacco di fork
  • sacco di reperti
  • Ogni directory prima che la profondità attuale viene colpito da trovare tutte le volte che c'è profondità totale al struttura del file (questo non dovrebbe essere un problema se si dispone di praticamente qualsiasi quantità di ram ...)

questo è un bene perché:

  • Esso utilizza bash e strumenti GNU di base
  • Può essere rotto ogni volta che si vuole (come si vede quello che stavi cercando per volare da)
  • Funziona per linea e non a trovare, i comandi in modo da successive don' t aspettare un ritrovamento e un ordinamento
  • Funziona in base alla reale separazione del file system, quindi se si ha una directory con una barra, essa non sarà elencata più in profondità di quanto non sia; se hai un separatore di percorso diverso configurato, stai ancora bene.
#!/bin/bash 
depth=0 

while find -mindepth $depth -maxdepth $depth | grep '.' 
do 
    depth=$((depth + 1)) 
done

È inoltre possibile usala su una linea abbastanza (?) Facilmente:

depth=0; while find -mindepth $depth -maxdepth $depth | grep --color=never '.'; do depth=$((depth + 1)); done 

Ma io preferisco piccoli script oltre scrivendo ...

3

è possibile utilizzare il comando trovare, find/path/to/dir tipo d così sotto esempio lista di directory nella directory corrente:

find . -type d 
Problemi correlati