2009-02-12 11 views
21

La lista de primer orden es importante, aquí. Además, limitar la profundidad buscada sería agradable.¿Cómo puedo hacer una lista recursiva de todos los directorios en una ubicación, en primer lugar?

$ 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 

Me gustaría hacer esto con un bash one-liner, si es posible. Si había un javascript-shell, me imagino algo así como

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

Podría extenderse sobre este para incluir el idioma de su elección, y el sistema operativo –

+0

Arg (Linux?)! Esta es una pregunta de "comunidad wiki". Molesto. –

+0

¿Qué hace que esta sea una pregunta wiki de la comunidad? –

Respuesta

15

El comando find apoya -printf opción que reconoce una gran cantidad de marcadores de posición.

Uno de estos marcadores de posición es %d que representa la profundidad de la ruta determinada, con respecto a dónde comenzó find.

Por lo tanto se puede utilizar después de sencilla de una sola línea:

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

Es bastante sencillo, y no depende de herramientas pesadas como perl.

como funciona:

  • que la lista de archivos, cada uno representa como una línea de dos campos
  • el primer campo contiene la profundidad, que se utiliza para (inversa) de clasificación numérica genera internamente y luego se corta distancia
  • resultante es la lista de archivos simple, un archivo por línea, en lo más profundo de primer orden
+0

Sencillo y elegante para un solo liner. ¡Gracias! – gnzg

2

Sin el merecido pedido: encontrar -maxdepth de tipo d

Para obtener el merecido pedido, usted tiene que hacer lo mismo recursividad , con este pequeño shellscript:

#!/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" 

A continuación, puede llamar a este script con los parámetros de directorio base y profundidad.

+0

Esto es exactamente lo que quiero, pero con un orden incorrecto. Cambié la pregunta para aclarar, ¡gracias! –

+0

ver mi adición! No terminé :) – ypnos

4

No creo que pueda hacerlo utilizando las utilidades incorporadas, ya que al atravesar una jerarquía de directorios casi siempre quiere una búsqueda en profundidad, ya sea de arriba hacia abajo o de abajo hacia arriba. Aquí hay un script en Python que le dará una búsqueda en amplitud:

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

Editar:

  1. Usando os.path -module en lugar de os.stat -función y stat -module.
  2. Usando list.pop y list.extend en lugar de los operadores del y +=.
3

He intentado encontrar una manera de hacer esto con find pero no parecen tener nada como una opción -breadth. Corto de escribir un parche para ello, pruebe el siguiente conjuro de comandos (por 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 

en cierto modo me topamos con este accidente, así que no sé si funciona en general (que estaba probando que sólo en la estructura de directorios específica que estabas preguntando acerca)

Si desea limitar la profundidad, poner una variable de contador en el bucle exterior, al igual que (también estoy añadiendo comentarios a éste):

# 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 

(reemplace el 2 en -lt 2 con la profundidad)

Básicamente esto implementa el algoritmo de búsqueda de amplitud estándar usando $LIST y $NLIST como una cola de nombres de directorio.Aquí está el último enfoque como una sola línea para una fácil de copiar y pegar:

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

Viéndolo de nuevo, esto definitivamente hace mi lista de "cosas que nunca deberían hacerse en Bash" ;-) –

+0

¿Puede también formatearlo no como una línea para facilitar la comprensión del código? ? (Pero sí, no hagas esto en bash :-) –

+0

Buena idea, ahora formateada y comentada ;-) –

19

Si desea hacerlo utilizando herramientas estándar, la siguiente tubería debería funcionar:

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

Es decir,

  1. encontrar e imprimir todos los directorios aquí en profundidad de primer orden
  2. contar el número de barras en cada directorio y usarlo sobre el camino
  3. ordenar por profundidad (es decir, número de barras)
  4. extraer solo la ruta.

Para limitar la profundidad encontrada, agregue el argumento -maxdepth al comando find.

Si desea que los directorios enumerados en el mismo orden que los encuentra, utilice "ordenar -n -s" en lugar de "ordenar -n"; el distintivo "-s" estabiliza el orden (es decir, conserva el orden de entrada entre los elementos que se comparan por igual).

+0

Agregue "2>/dev/null" al comando find, es decir, find. -type d 2>/dev/null Se asegurará de que el error de búsqueda no arruine los resultados. –

+0

¿Qué tal ordenar alfabéticamente? – mr5

+0

No funciona si los dirnames tienen espacios. Por ejemplo, si un directorio es "/ data/Mundial/Trinidad \ y \ Tobago /", tendrá solo "/ data/arbol/Mundial/Trinidad". – alemol

1

Aquí hay una manera posible, usando find. No he probado a fondo, así que ten cuidado de usuario ...

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

Algo como esto:

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

Mi sensación es que esta es una solución mejor que anteriormente citados. Implica grep y tal y un bucle, pero me parece que funciona muy bien, específicamente para los casos en los que desea que las cosas estén almacenadas en búfer y no en el búfer de búsqueda completa.

Es más intensivo de los recursos debido a:

  • Mucha bifurcan
  • Un montón de hallazgos
  • Cada directorio antes de la profundidad actual es golpeado por encontrar tantas veces como es la profundidad total de la estructura de archivos (esto no debería ser un problema si usted tiene prácticamente cualquier cantidad de ram ...)

esto es bueno porque:

  • Utiliza bash y herramientas GNU básicos
  • Se puede romperse cuando lo desee (como se ve lo que estabas buscando mosca por)
  • Funciona por línea y no por hallazgo, los comandos de manera posteriores don' t tiene que esperar un hallazgo y una ordenación
  • Funciona en función de la separación real del sistema de archivos, por lo que si tiene un directorio con una barra oblicua, no aparecerá en una lista más profunda de lo que es; si tiene configurado un separador de ruta diferente, todavía está bien.
#!/bin/bash 
depth=0 

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

También puede encajar en una sola línea más o menos (?) Fácilmente:

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

Pero prefiero más pequeños scripts escribiendo ...

3

puede utilizar comandos encontrar, hallazgo/ruta/a/d de tipo dir Así que a continuación ejemplo lista de directorios en el directorio actual:

find . -type d 
Cuestiones relacionadas