Comment boucler sur des répertoires sous Linux?


168

J'écris un script en bash sur Linux et j'ai besoin de parcourir tous les noms de sous-répertoires dans un répertoire donné. Comment puis-je parcourir ces répertoires (et ignorer les fichiers normaux)?

Par exemple:
le répertoire donné /tmp/
contient les sous-répertoires suivants:/tmp/A, /tmp/B, /tmp/C

Je veux récupérer A, B, C.


Réponses:


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

Une brève explication:

  • find trouve des fichiers (bien évidemment)

  • .est le répertoire actuel, qui après le cdest /tmp(à mon humble avis, c'est plus flexible que d'avoir /tmpdirectement dans la findcommande. Vous n'avez qu'un seul endroit, le cd, à changer, si vous voulez que plus d'actions aient lieu dans ce dossier)

  • -maxdepth 1et -mindepth 1assurez-vous que findne regarde que dans le répertoire courant et ne s'inclut pas .dans le résultat

  • -type d recherche uniquement les répertoires

  • -printf '%f\n imprime uniquement le nom du dossier trouvé (plus une nouvelle ligne) pour chaque appel.

Et voilà!


Au fait: notez que toutes les réponses trouveront également des dossiers cachés (c'est-à-dire des dossiers commençant par un point)! Si vous ne le souhaitez pas, ajoutez `-regex '\ ./ [^ \.]. *' 'Avant les options printf ou exec.
Boldewyn

1
Utile - si vous avez besoin d'exécuter une seule commande pour chaque hit ;-) - Seulement j'ai plusieurs commandes à exécuter :-(.
Martin

Pas de problème: adaptez cette solution: transnum.blogspot.de/2008/11/… Dans la while..doneboucle, vous pouvez devenir fou.
Boldewyn

1
C'est une très bonne méthode pour certains cas d'utilisation; findL' -execoption de vous permet d'exécuter n'importe quelle commande pour chaque fichier / répertoire.
jtpereyda le

451

Toutes les réponses utilisées jusqu'à présent find, alors en voici une avec juste le shell. Pas besoin d'outils externes dans votre cas:

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

18
+1 - Pourquoi s'embêter findquand vous pouvez ajouter une barre oblique à un joker
Tobias Kienzler

19
il en for dir in */; do echo $dir; doneva de même pour les répertoires du répertoire courant.
Ehtesh Choudhury

41
Ce serait bien s'il pouvait contenir une explication de ce que dir=${dir%*/}et echo ${dir##*/}fait.
Jeremy S.

11
C'est parce que la variable dir inclura la barre oblique inverse à la fin. Il le supprime simplement pour avoir un nom propre. % supprimera le / à la fin. ## supprimera le / au début. Ce sont des opérateurs pour gérer les sous-chaînes.
sfratini

8
S'il n'y a pas de correspondance, la boucle sera déclenchée avec /tmp/*/, il serait sage d'inclure une vérification pour voir si le répertoire existe réellement.
Jrs42

41

Vous pouvez parcourir tous les répertoires, y compris les répertoires cachés (commençant par un point) avec:

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

remarque: l' utilisation de la liste ne */ .*/fonctionne dans zsh que s'il existe au moins un répertoire caché dans le dossier. En bash, il montrera aussi .et..


Une autre possibilité pour bash d'inclure des répertoires cachés serait d'utiliser:

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

Si vous souhaitez exclure les liens symboliques:

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

Pour afficher uniquement le nom du répertoire de fin (A, B, C comme interrogé) dans chaque solution, utilisez ceci dans les boucles:

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

Exemple (cela fonctionne également avec les répertoires contenant des espaces):

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

16

Fonctionne avec les répertoires contenant des espaces

Inspiré par 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)

Message d'origine (ne fonctionne pas avec les espaces)

Inspiré de Boldewyn : Exemple de boucle avec findcommande.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done


6

La technique que j'utilise le plus souvent est find | xargs. Par exemple, si vous souhaitez rendre tous les fichiers de ce répertoire et de tous ses sous-répertoires lisibles par le monde, vous pouvez faire:

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

L' -print0option se termine par un caractère NULL au lieu d'un espace. L' -0option divise son entrée de la même manière. C'est donc la combinaison à utiliser sur les fichiers avec des espaces.

Vous pouvez imaginer cette chaîne de commandes comme prenant chaque sortie de ligne findet la collant à la fin d'une chmodcommande.

Si la commande que vous souhaitez exécuter en tant qu'argument au milieu plutôt qu'à la fin, vous devez être un peu créatif. Par exemple, j'avais besoin de changer dans chaque sous-répertoire et d'exécuter la commande latemk -c. J'ai donc utilisé (de Wikipedia ):

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

Cela a pour effet de for dir $(subdirs); do stuff; done, mais est sans danger pour les répertoires avec des espaces dans leurs noms. De plus, les appels séparés à stuffsont effectués dans le même shell, c'est pourquoi dans ma commande nous devons revenir au répertoire courant avec popd.


3

une boucle de bash minimale sur laquelle vous pouvez construire (basée sur la réponse de ghostdog74)

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

pour compresser tout un tas de fichiers par répertoire

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               

Cela répertorie tous les fichiers de directory, pas seulement les sous-répertoires.
dexteritas

2

find . -type d -maxdepth 1


2
cette réponse est bonne, sauf que je reçois les chemins complets, alors que je ne veux que les noms des répertoires.
Erik Sapir le

2

Si vous souhaitez exécuter plusieurs commandes dans une boucle for, vous pouvez enregistrer le résultat de findwith mapfile(bash> = 4) en tant que variable et parcourir le tableau avec ${dirlist[@]}. Il fonctionne également avec des répertoires contenant des espaces.

La findcommande est basée sur la réponse de Boldewyn. Vous trouverez de plus amples informations sur la findcommande ici.

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.