web-dev-qa-db-fra.com

Boucle sur les répertoires dans Bash

J'ai une question fondamentale sur le fonctionnement de bash et une question pratique connexe.

Question fondamentale: supposons que je suis dans un répertoire qui a trois sous-répertoires: a, b et c.

le code

for dir in $(ls)
do 
    echo $dir
done

crache:

a b c
a b c
a b c

i.e, dir stocke toujours une liste de tous les fichiers/répertoires de ma cwd. Ma question est: pourquoi dans le monde cela serait-il pratique? À mon avis, il est beaucoup plus utile et intuitif d’avoir dir de stocker chaque élément à la fois, c’est-à-dire que j’aimerais avoir une sortie.

a
b
c

En outre, selon l'une des réponses - il est faux d'utiliser for dir in $(ls), mais lorsque j'utilise for dir in $(ls -l), je reçois encore plus de copies de a b c (plus qu'il n'y a de répertoires/fichiers dans le cwd). Pourquoi donc?

Ma deuxième question est pratique: comment puis-je parcourir tous les répertoires (pas les fichiers!) De ma cwd qui commencent par majuscule W? J'ai commencé avec 

for dir in `ls -l W*`

mais cela échoue car a) la raison de la question 1 et b) car elle n'exclut pas les fichiers. Suggestions appréciées.

13
alexvas

Ne jamais analyser la sortie de ls comme ceci ( Pourquoi ne pas analyser la sortie de ls (1) ).

De plus, votre syntaxe est fausse. Vous ne voulez pas dire (), vous voulez dire $().

Cela dit, vous feriez une boucle sur les répertoires commençant par W (ou utilisez plutôt la commande find, selon votre scénario):

for path in /my/path/W*; do
    [ -d "${path}" ] || continue # if not a directory, skip
    dirname="$(basename "${path}")"
    do_stuff
done

Quant à la sortie que vous obtenez de la mauvaise boucle ls, elle ne devrait pas ressembler à cela. Ceci est la sortie attendue et montre pourquoi vous ne voulez pas utiliser ls en premier lieu:

$ find
.
./c
./a
./foo bar
./b

$ type ls
ls is hashed (/bin/ls)

$ for x in $(ls); do echo "${x}"; done
a
b
c
foo
bar
36

Cela devrait fonctionner:

shopt -s nullglob   # empty directory will return empty list
for dir in ./*/;do
    echo "$dir"         # dir is directory only because of the / after *
done

Pour être également récursif dans les sous-répertoires, utilisez globstar:

shopt -s globstar nullglob
for dir in ./**/;do
    echo "$dir" # dir is directory only because of the / after **
done

Vous pouvez faire en sorte que la méthode de @Adrian Frühwirths soit récursive en sous-répertoires en utilisant aussi globstar:

shopt -s globstar
for dir in ./**;do
    [[ ! -d $dir ]] && continue # if not directory then skip
    echo "$dir"
done

De Bash Manual:

globstar

Si défini, le modèle ‘**’ utilisé dans un contexte d’extension de nom de fichier sera correspond à tous les fichiers et à zéro ou plusieurs répertoires et sous-répertoires. Si le motif est suivi de ‘/’, uniquement des répertoires et des sous-répertoires rencontre.

nullglob

Si défini, Bash autorise le développement de modèles de nom de fichier qui ne correspondent à aucun fichier à une chaîne nulle, plutôt qu'eux-mêmes.

10
Jahid

Eh bien, vous savez que ce que vous voyez n’est pas ce que vous attendez. La sortie que vous voyez ne provient pas de la commande echo, mais de la commande dir.

Essayez ce qui suit:

ls -1 | while read line; do 

   if [-d "$line" ] ; then 
      echo $line
   fi

done


for files in $(ls) ; do

   if [-d "$files" ] ; then 
      echo $files
   fi

done
0
user1587276