¿Cómo recorrer directorios en Linux?

168

Estoy escribiendo un script en bash en Linux y necesito revisar todos los nombres de subdirectorios en un directorio dado. ¿Cómo puedo recorrer estos directorios (y omitir archivos normales)?

Por ejemplo:
el directorio dado /tmp/
tiene los siguientes subdirectorios:/tmp/A, /tmp/B, /tmp/C

Quiero recuperar A, B, C.

Erik Sapir
fuente

Respuestas:

129
cd /tmp
find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n'

Una breve explicación:

  • find encuentra archivos (obviamente)

  • .es el directorio actual, que después de cdis /tmp(en mi humilde opinión esto es más flexible que tener /tmpdirectamente en el findcomando. Solo tiene un lugar, el cd, para cambiar, si desea que se realicen más acciones en esta carpeta)

  • -maxdepth 1y -mindepth 1asegúrese de que findsolo se vea en el directorio actual y no se incluya .en el resultado

  • -type d busca solo directorios

  • -printf '%f\n imprime solo el nombre de la carpeta encontrada (más una nueva línea) para cada hit.

Et voilà!

Boldewyn
fuente
Por cierto: ¡tenga en cuenta que todas las respuestas también encontrarán carpetas ocultas (es decir, carpetas que comienzan con un punto)! Si no desea esto, agregue '-regex' \ ./ [^ \.]. * '' Antes de las opciones printf o exec.
Boldewyn
1
Útil - si necesita ejecutar solo un comando para cada golpe ;-) - Solo tengo varios comandos para ejecutar :-(.
Martin
No hay problema: Adapte esta solución: transnum.blogspot.de/2008/11/… Dentro del while..donebucle puede volverse loco.
Boldewyn
1
Este es un método muy bueno para algunos casos de uso; findLa -execopción 'le permite ejecutar cualquier comando para cada archivo / directorio.
jtpereyda
451

Todas las respuestas hasta ahora se usan find, así que aquí hay una con solo el shell. No necesita herramientas externas en su caso:

for dir in /tmp/*/     # list directories in the form "/tmp/dirname/"
do
    dir=${dir%*/}      # remove the trailing "/"
    echo ${dir##*/}    # print everything after the final "/"
done
ghostdog74
fuente
18
+1 - ¿Por qué molestarse findcuando puedes agregar una barra a un comodín?
Tobias Kienzler
19
así for dir in */; do echo $dir; donees para los directorios en el directorio actual.
Ehtesh Choudhury
41
Sería bueno si pudiera contener una explicación de lo dir=${dir%*/}y echo ${dir##*/}lo está haciendo.
Jeremy S.
11
Esto se debe a que el directorio variable incluirá la barra diagonal inversa al final. Simplemente lo está eliminando para tener un nombre limpio. % eliminará el / al final. ## eliminará / al principio. Estos son operadores para manejar subcadenas.
sfratini
8
Si no hay coincidencia, se activará el bucle /tmp/*/, sería conveniente incluir una verificación para ver si el directorio realmente existe.
Jrs42
41

Puede recorrer todos los directorios, incluidos los directorios ocultos (comenzando con un punto) con:

for file in */ .*/ ; do echo "$file is a directory"; done

nota: el uso de la lista */ .*/funciona en zsh solo si existe al menos un directorio oculto en la carpeta. En bash se mostrará también .y..


Otra posibilidad para que bash incluya directorios ocultos sería usar:

shopt -s dotglob;
for file in */ ; do echo "$file is a directory"; done

Si desea excluir enlaces simbólicos:

for file in */ ; do 
  if [[ -d "$file" && ! -L "$file" ]]; then
    echo "$file is a directory"; 
  fi; 
done

Para generar solo el nombre del directorio final (A, B, C como se cuestionó) en cada solución, use esto dentro de los bucles:

file="${file%/}"     # strip trailing slash
file="${file##*/}"   # strip path and leading slash
echo "$file is the directoryname without slashes"

Ejemplo (esto también funciona con directorios que contienen espacios):

mkdir /tmp/A /tmp/B /tmp/C "/tmp/ dir with spaces"
for file in /tmp/*/ ; do file="${file%/}"; echo "${file##*/}"; done
rubo77
fuente
16

Funciona con directorios que contienen espacios.

Inspirado por Sorpigal

while IFS= read -d $'\0' -r file ; do 
    echo $file; ls $file ; 
done < <(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d -print0)

Publicación original (no funciona con espacios)

Inspirado por Boldewyn : Ejemplo de bucle con findcomando.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done
zpon
fuente
9
find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n"
Ignacio Vazquez-Abrams
fuente
Eso también es bueno y elimina la necesidad de hacerlo basename. Preferiría esto sobre mi respuesta.
Boldewyn
6

La técnica que uso con más frecuencia es find | xargs. Por ejemplo, si desea que todos los archivos de este directorio y todos sus subdirectorios sean legibles en todo el mundo, puede hacer lo siguiente:

find . -type f -print0 | xargs -0 chmod go+r
find . -type d -print0 | xargs -0 chmod go+rx

La -print0opción termina con un carácter NULL en lugar de un espacio. La -0opción divide su entrada de la misma manera. Entonces esta es la combinación para usar en archivos con espacios.

Puede imaginar esta cadena de comandos tomando cada salida de línea findy pegándola al final de un chmodcomando.

Si el comando que desea ejecutar como argumento en el medio en lugar de al final, debe ser un poco creativo. Por ejemplo, necesitaba cambiar a cada subdirectorio y ejecutar el comando latemk -c. Entonces usé (de Wikipedia ):

find . -type d -depth 1 -print0 | \
    xargs -0 sh -c 'for dir; do pushd "$dir" && latexmk -c && popd; done' fnord

Esto tiene el efecto de for dir $(subdirs); do stuff; done, pero es seguro para directorios con espacios en sus nombres. Además, las llamadas por separado stuffse realizan en el mismo shell, por lo que en mi comando tenemos que volver al directorio actual con popd.

Matthew Leingang
fuente
3

un bucle bash mínimo del que puede construir (basado en la respuesta ghostdog74)

for dir in directory/*                                                     
do                                                                                 
  echo ${dir}                                                                  
done

comprimir un montón de archivos por directorio

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               
Harry Moreno
fuente
Esto enumera todos los archivos directory, no solo los subdirectorios.
dexteritas
2

find . -type d -maxdepth 1

Anónimo
fuente
2
esta respuesta es buena, excepto que recibo las rutas completas, mientras que solo quiero los nombres de los directorios.
Erik Sapir
2

Si desea ejecutar múltiples comandos en un bucle for, puede guardar el resultado de findwith mapfile(bash> = 4) como una variable y recorrer la matriz con ${dirlist[@]}. También funciona con directorios que contienen espacios.

El findcomando se basa en la respuesta de Boldewyn. Se findpuede encontrar más información sobre el comando allí.

IFS=""
mapfile -t dirlist < <( find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n' )
for dir in ${dirlist[@]}; do
    echo ">${dir}<"
    # more commands can go here ...
done
dexteritas
fuente