¿Cómo recorro solo los directorios en bash?

251

Tengo una carpeta con algunos directorios y algunos archivos (algunos están ocultos, comenzando con punto).

for d in *; do
 echo $d
done

recorrerá todos los archivos, pero quiero recorrer solo los directorios. ¿Cómo puedo hacer eso?

rubo77
fuente

Respuestas:

334

Puede especificar una barra diagonal al final para que coincida solo con los directorios:

for d in */ ; do
    echo "$d"
done
choroba
fuente
77
Tenga en cuenta que también incluye enlaces simbólicos a directorios.
Stéphane Chazelas
1
Entonces, ¿cómo excluirías los enlaces simbólicos?
rubo77
3
@ rubo77: puede probar [[ -L $d ]]si $ d es un enlace simbólico.
choroba
1
@AsymLabs Eso es incorrecto, set -Psolo afecta a los comandos que cambian de directorio. sprunge.us/TNac
Chris Down
44
@choroba: [[ -L "$f" ]]no excluirá los enlaces simbólicos en este caso con */, debe quitar la barra inclinada final [[ -L "${f%/}" ]](vea Prueba para el enlace con la barra inclinada final )
rubo77
78

Puedes probar con -d:

for f in *; do
    if [ -d "$f" ]; then
        # $f is a directory
    fi
done

Este es uno de los operadores de prueba de archivos .

encerrada dorada
fuente
8
Tenga en cuenta que incluirá enlaces simbólicos a directorios.
Stéphane Chazelas
21
if [[ -d "$f" && ! -L "$f" ]]excluirá enlaces simbólicos
rubo77
Se romperá en el directorio vacío. if [[ "$f" = "*" ]]; then continue; fi
Piskvor
30

Tenga en cuenta que la solución de choroba, aunque elegante, puede provocar un comportamiento inesperado si no hay directorios disponibles dentro del directorio actual. En este estado, en lugar de omitir el forciclo, bash ejecutará el ciclo exactamente una vez donde des igual a */:

#!/usr/bin/env bash

for d in */; do
    # Will print */ if no directories are available
    echo $d
done

Recomiendo usar lo siguiente para protegerse contra este caso:

#!/usr/bin/env bash

for f in *; do
    if [ -d ${f} ]; then
        # Will not run if no directories are available
        echo $f
    fi
done

Este código recorrerá todos los archivos en el directorio actual, verificará si fes un directorio, luego hará eco fsi la condición devuelve verdadero. Si fes igual a */, echo $fno se ejecutará.

emagdne
fuente
3
Mucho más fácil de hacer shopt -s nullglob.
choroba
21

Si necesita seleccionar archivos más específicos que solo los directorios usan findy pasarlos a while read:

shopt -s dotglob
find * -prune -type d | while IFS= read -r d; do 
    echo "$d"
done

Se usa shopt -u dotglobpara excluir directorios ocultos (o setopt dotglob/ unsetopt dotgloben zsh).

IFS=para evitar dividir nombres de archivo que contengan uno de los siguientes $IFS, por ejemplo:'a b'

vea la respuesta de AsymLabs a continuación para más findopciones


editar:
en caso de que necesite crear un valor de salida desde el ciclo while, puede eludir la subshell adicional con este truco:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -prune -type d)
rubo77
fuente
2
Encontré esta solución en: stackoverflow.com/a/8489394/1069083
rubo77
11

Puedes usar pure bash para eso, pero es mejor usar find:

find . -maxdepth 1 -type d -exec echo {} \;

(Encontrar adicionalmente incluirá directorios ocultos)

prisa
fuente
8
Tenga en cuenta que no incluye enlaces simbólicos a directorios. Puede usar shopt -s dotglobpara bashincluir directorios ocultos. El tuyo también incluirá .. También tenga en cuenta que -maxdepthno es una opción estándar ( -prunees).
Stéphane Chazelas
2
La dotglobopción es interesante pero dotglobsolo se aplica al uso de *. find .siempre incluirá directorios ocultos (y el directorio actual también)
rubo77
6

Esto se hace para encontrar directorios visibles y ocultos dentro del directorio de trabajo actual, excluyendo el directorio raíz:

simplemente recorrer los directorios:

 find -path './*' -prune -type d

para incluir enlaces simbólicos en el resultado:

find -L -path './*' -prune -type d

hacer algo a cada directorio (excluyendo enlaces simbólicos):

find -path './*' -prune -type d -print0 | xargs -0 <cmds>

para excluir directorios ocultos:

find -path './[^.]*' -prune -type d

para ejecutar múltiples comandos en los valores devueltos (un ejemplo muy artificial):

find -path './[^.]*' -prune -type d -print0 | xargs -0 -I '{}' sh -c \
"printf 'first: %-40s' '{}'; printf 'second: %s\n' '{}'"

en lugar de 'sh -c' también puede usar 'bash -c', etc.

AsymLabs
fuente
¿Qué sucede si el eco '* /' en 'para d en echo * /' contiene, por ejemplo, 60,000 directorios?
AsymLabs
Obtendrá el error "Demasiados archivos", pero eso se puede resolver: Cómo sortear "Demasiados archivos abiertos" en debian
rubo77
1
El ejemplo de xargs solo funcionaría para un subconjunto de nombres de directorio 9e.g. aquellos con espacios o líneas nuevas no funcionarían). Es mejor usarlo ... -print0 | xargs -0 ...si no sabe cuáles son los nombres exactos.
Anthon
1
@ rubo77 agregó una forma de excluir archivos / directorios ocultos y la ejecución de múltiples comandos; por supuesto, esto también se puede hacer con un script.
AsymLabs
1
Agregué un ejemplo en mi respuesta en la parte inferior, cómo hacer algo en cada directorio usando una función confind * | while read file; do ...
rubo77
3

Puede recorrer todos los directorios, incluidos los directorios ocultos (comenzando con un punto) en una línea y múltiples comandos con:

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

nota: el uso de la lista */ .*/funciona en bash, pero también muestra las carpetas .y ..mientras está en zsh no las mostrará, pero arrojará un error si no hay ningún archivo oculto en la carpeta

Una versión más limpia que incluirá directorios ocultos y excluirá ../ estará con la opción dotglob:

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

(o setopt dotgloben zsh)

puedes desarmar dotglob con

shopt -u dotglob
revs rubo77
fuente
2

Esto incluirá la ruta completa en cada directorio de la lista:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done
rubo77
fuente
1
se rompe cuando se usan carpetas con espacios
Alejandro Sazo
0

Use findcon -execpara recorrer los directorios y llamar a una función en la opción exec:

dosomething () {
  echo "doing something with $1"
}
export -f dosomething
find -path './*' -prune -type d -exec bash -c 'dosomething "$0"' {} \;

Use shopt -s dotglobo shopt -u dotglobpara incluir / excluir directorios ocultos

rubo77
fuente
0

Esto enumera todos los directorios junto con el número de subdirectorios en una ruta determinada:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done
pabloa98
fuente
-1
ls -d */ | while read d
do
        echo $d
done
dcat
fuente
This is one directory with spaces in the name- pero se analiza como múltiple.
Piskvor
-5
ls -l | grep ^d

o:

ll | grep ^d

Puedes configurarlo como un alias

user250676
fuente
1
Desafortunadamente, no creo que esto responda la pregunta, que era "Quiero recorrer solo los directorios", que es ligeramente diferente de esta lsrespuesta basada en la lista , que enumera los directorios.
Jeff Schaller
1
Nunca es una buena idea analizar el resultado de ls: mywiki.wooledge.org/ParsingLs
codeforester