187 votes

Parcourir en boucle tous les fichiers avec une extension spécifique

for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Je veux parcourir en boucle chaque fichier du dossier actuel et vérifier s'il correspond à une extension spécifique. Le code ci-dessus ne fonctionne pas, savez-vous pourquoi ?

294voto

chepner Points 54078

Pas besoin de faire des tours de passe-passe :

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

La garde garantit que s'il n'y a pas de fichier correspondant, la boucle se terminera sans essayer de traiter un nom de fichier inexistant. *.java .

Sur bash (ou des shells supportant quelque chose de similaire), vous pouvez utiliser la fonction nullglob option pour simplement ignorer une correspondance ratée et ne pas entrer dans le corps de la boucle.

shopt -s nullglob
for i in *.java; do
    ...
done

Quelques détails supplémentaires sur le break -vs- continue discussion dans les commentaires. Je considère qu'il est quelque peu hors de propos de savoir si vous utilisez break ou continue parce que la première boucle essaie de faire la distinction entre deux cas :

  1. *.java n'a pas de correspondance, et est donc traité comme du texte littéral.
  2. *.java avait au moins une correspondance, et cette correspondance pouvait inclure une entrée nommée *.java .

Dans le cas n°1, break est bien, car il n'y a pas d'autres valeurs de $i à venir, et break et continue serait équivalent (bien que je trouve break plus explicite ; vous sortez de la boucle, vous ne vous contentez pas d'attendre que la boucle se termine passivement).

Dans le cas n° 2, vous devez encore effectuer le filtrage nécessaire sur toutes les correspondances possibles. En tant que tel, le choix de break ou continue est moins important que celui qui test ( -f , -d , -e etc.) vous vous adressez à $i Ce qui, selon moi, n'est pas la bonne façon de déterminer si vous êtes entré dans la boucle "incorrectement" en premier lieu.

C'est-à-dire que je ne veux pas être dans la position d'examiner la valeur de $i du tout dans le cas n°1, et dans le cas n°2, ce que vous faites avec la valeur a plus à voir avec votre logique d'entreprise pour chaque fichier, plutôt qu'avec la logique de la fonction en sélectionnant les fichiers à traiter en premier lieu. Je préfère laisser cette logique à l'utilisateur individuel, plutôt que d'exprimer un choix ou l'autre dans la question.


En passant, zsh permet d'effectuer ce type de filtrage dans le glob lui-même. Vous pouvez faire correspondre seulement les fichiers réguliers se terminant par .java (et désactiver le comportement par défaut qui consiste à traiter les motifs non appariés comme une erreur, plutôt que comme du texte littéral) à l'aide de la commande

for f in *.java(.N); do
  ...
done

Avec ce qui précède, vous êtes garanti que si vous atteignez le corps de la boucle, alors $f se développe vers le nom d'un fichier ordinaire. Le site . fait *.java ne correspondent qu'aux fichiers réguliers, et l'option N fait en sorte qu'une correspondance échouée ne se développe pas au lieu de produire une erreur.

Il existe également d'autres qualificatifs globaux pour effectuer toutes sortes de filtrages sur les expansions de noms de fichiers. (J'aime plaisanter en disant que zsh L'expansion des globes remplace la nécessité d'utiliser la fonction find du tout).

31voto

luismartingil Points 101

Ajouter récursivement des sous-dossiers,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done

23voto

Benny Points 501

Boucle sur tous les fichiers se terminant par : .img , .bin , .txt et imprimer le nom du fichier :

for i in *.img *.bin *.txt;
do
  echo "$i"
done

Ou de manière récursive (recherche également dans tous les sous-répertoires) :

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done

14voto

umläute Points 5746

La bonne réponse est celle de @chepner

EXT=java
for i in *.${EXT}; do
    ...
done

Cependant, voici une petite astuce pour vérifier si un nom de fichier possède une extension donnée :

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done

3voto

peteches Points 3009

Comme le dit @chepner dans son commentaire, vous comparez $i à une chaîne fixe.

Pour étendre et rectifier la situation, vous devez utiliser [[ ]] avec l'opérateur regex =~.

eg :

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

la regex à droite de =~ est testée par rapport à la valeur de l'opérateur de gauche et ne doit pas être citée, ( citée n'entraînera pas d'erreur mais sera comparée à une chaîne fixe et donc échouera très probablement".

mais la réponse de @chepner ci-dessus, en utilisant le glob, est un mécanisme beaucoup plus efficace.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X