2009-10-15 10 views
13

¿Existe una manera fácil de enumerar solo directorios bajo un directorio determinado en Linux? Para explicar mejor, que puedo hacer:Lista de todos los subdirectorios de hojas en Linux

find mydir -type d 

que da:

mydir/src 
mydir/src/main 
mydir/bin 
mydir/bin/classes 

Lo que quiero en cambio, es:

mydir/src/main 
mydir/bin/classes 

que pueda hacer esto en una escritura del golpe que se repite más de las líneas y elimina la línea anterior si la siguiente línea contiene la ruta, pero me pregunto si hay un método más simple que no utilice bucles bash.

+0

Con su ejemplo, 'find midirectorio -mindepth 2 - tipo d' funcionaría, pero no funcionará cuando tenga múltiples profundidades máximas. ¿Realmente desea enumerar solo directorios que no contienen otros directorios o está buscando ver un nivel particular de estructura de directorios? – Cascabel

+0

Sí, gracias por aclarar: mi ejemplo fue un ejemplo simplista. De hecho, estaba buscando una solución más general. Además, estoy buscando ver la estructura general del directorio, realmente no me importan los archivos en los directorios (así también, leaf significa "directorio de hojas" en este contexto). Gracias. – amol

Respuesta

10
find . -type d | sort | awk '$0 !~ last "/" {print last} {last=$0} END {print last}' 
+0

Gracias, el tipo de solución que estaba buscando :) – amol

+0

Esto producirá resultados correctos, pero sin clasificar, sin el 'orden' (para que pueda ponerlo al final, en su lugar, si lo desea). –

+0

Esto no funciona si tiene dos directorios: foo/bar y foo/bar_baz. foo/bar no se imprimirá. – mattismyname

4

No se me ocurre nada que pueda hacer esto sin un bucle. Por lo tanto, aquí hay algunos bucles:

Esto muestra los directorios de la hoja del directorio actual, independientemente de su profundidad:

for dir in $(find -depth -type d); do [[ ! $prev =~ $dir ]] && echo "$dir" ; prev="$dir"; done 

Esta versión maneja correctamente los nombres de directorio espacios que contienen:

saveIFS=$IFS; IFS=$'\n'; for dir in $(find -depth -type d); do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done; IFS=$saveIFS 

Aquí es una versión que usa sugerencia de Jefromi:

find -depth -type d | while read dir; do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done 
+0

El OP preguntaba si había una manera más simple que hacer un bucle sobre la salida, no cómo escribir el ciclo. Dicho esto, también es una buena idea usar 'find ... | mientras lee dir' en lugar de 'para dir in $ (...)', porque no tiene que hacer todo el hallazgo antes de imprimir nada. – Cascabel

+0

@Jefromi: Piping 'find' en' while' también maneja nombres con espacios correctamente de forma gratuita. –

+0

Bueno, evitar bucles obviamente no es un "requisito" difícil, pero esperaba que hubiera una forma más simple e intuitiva de hacerlo sin bucles. Gracias por el hallazgo | mientras que la información sin embargo. – amol

0

Esto sigue siendo un bucle, ya que utiliza el comando sucursal en sed:

find -depth -type d |sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D' 

Sobre la base de un guión en info sed (uniq trabajo por igual).

Editar Aquí está la secuencia de comandos sed estallado con comentarios (copiados de info sed y modificado):

# copy the pattern space to the hold space 
h 

# label for branch (goto) command 
:b 
# on the last line ($) goto the end of 
# the script (b with no label), print and exit 
$b 
# append the next line to the pattern space (it now contains line1\nline2 
N 
# if the pattern space matches line1 with the last slash and whatever comes after 
# it followed by a newline followed by a copy of the part before the last slash 
# in other words line2 is different from line one with the last dir removed 
# see below for the regex 
/^\(.*\)\/.*\n\1$/ { 
    # Undo the effect of 
    # the n command by copying the hold space back to the pattern space 
    g 
    # branch to label b (so now line2 is playing the role of line1 
    bb 
} 
# If the `N' command had added the last line, print and exit 
# (if this is the last line then swap the hold space and pattern space 
# and goto the end (b without a label) 
$ { x; b } 

# The lines are different; print the first and go 
# back working on the second. 
# print up to the first newline of the pattern space 
P 
# delete up to the first newline in the pattern space, the remainder, if any, 
# will become line1, go to the top of the loop 
D 

Esto es lo que la expresión regular está haciendo:

  • / - iniciar un patrón
  • ^ - coincide con el comienzo de la línea
  • \( - iniciar un grupo de captura (subexpresión de referencia atrás)
  • .* - cero o más (*) de cualquier carácter (.)
  • \) - extremo grupo de captura
  • \/ - una barra (/) (escapado con \)
  • .* - cero o más de cualquier carácter
  • \n - una nueva línea
  • \1 - una copia de la referencia retrospectiva (que en este caso es la que estaba entre el comienzo de la línea y la última barra)
  • $ - coincide con el final de la línea
  • / - terminar el patrón
+0

Su sugerencia funciona muy bien, pero me resulta difícil de entender. Gracias, sin embargo, voy a tratar de averiguar lo que significan las opciones sed. – amol

0

creo que se puede ver en todos los directorios y luego redirigir que la salida y usar xargs para el recuento de los archivos de número para cada subdirectorios, cuando no hay subdirectorio (xargs encuentran SUBDIR de tipo D | wc -l ... algo así, no puedo probar ahora mismo) has encontrado una hoja.

Esto sigue siendo un ciclo.

+0

Ah ... quizás no explique lo que significaba "hoja" correctamente. Quise decir "hoja dir", así que si todo un directorio tuviera archivos, aún calificaría como una "hoja" para mi problema. – amol

+0

ouhla, yo ' estoy cansado, busco directorios debajo y wc -l == 0, ese debería ser el truco ... – LB40

4

Si está buscando algo visual, tree -d es agradable.

 
drinks 
|-- coke 
| |-- cherry 
| `-- diet 
|  |-- caffeine-free 
|  `-- cherry 
|-- juice 
| `-- orange 
|  `-- homestyle 
|   `-- quart 
`-- pepsi 
    |-- clear 
    `-- diet 
+0

Gracias, esto también es útil (no para mi problema actual, pero es bueno saberlo) Me pregunto si el árbol es una adición reciente. Recuerdo que necesitaba algo como esto (estaba en RHEL en ese momento, ahora en Ubuntu) hace un año y terminé usando el script en http://centerkey.com/tree. – amol

10

Si desea que sólo los directorios de la hoja (directorios que no contienen ningún subdirectorio), mira this other question. La respuesta también explains it, pero en resumen es:

find . -type d -links 2 
+1

En Mac al menos, los enlaces incluyen directorios actuales, principales y secundarios, pero también archivos. Entonces esta solución solo funciona para directorios de hojas vacías. –

+0

Acabo de notar que no funciona en montajes SMB tampoco. Pero lo hace en un montaje NFS. Pero en situaciones donde funciona (directorios Linux locales o NFS), ciertamente es la solución más simple. – mivk

0

Pruebe lo siguiente de una sola línea (probado en Linux & OS X):

find . -type d -execdir sh -c 'test -z "$(find "{}" -mindepth 1 -type d)" && echo $PWD/{}' \; 
Cuestiones relacionadas