Archives du mot-clé bash

[Avancé] rechercher et agir sur les fichiers en double sous Linux

Cet article vient en complément de mon article Comment appliquer récursivement une instruction à des fichiers sous Linux, pour préciser comment formuler la recherche.

Sous Linux, lorsqu’au sein d’un même répertoire, deux fichiers s’enregistrent sous le même nom, le système rajoute automatiquement entre la fin du nom et l’extension un nombre entre parenthèses qui s’incrémente automatiquement avec le nombre de copies. Par exemple si un de vos logiciels enregistre plusieurs fois le fichier « I_love_linux.txt » dans ce répertoire, la deuxième copie sera enregistrée avec le nom « I_love_linux(1).txt », la troisième avec « I_love_linux(2).txt », et ainsi de suite.

Pour des raisons élémentaires de gain de place, il peut être utile de supprimer ces fichiers, et le but de cet article est de trouver une commande qui fait ça automatiquement. L’astuce est de combiner correctement les caractères universels de la commande find. Pour rappel:

  • le caractère ‘*’ remplace n’importe quelle chaine de caractères.
  • le caractère ‘?’ remplace n’importe quel caractère.

Donc dans le cas où aucun fichier ne porte de nom avec quelque chose entre parenthèse dedans sinon les fichiers copies, il suffira d’utiliser la commande find avec le pattern « *(*).* ». Dans le cas contraire, il faudra d’abord rechercher les fichiers compris entre 1 et 9, puis ceux entre 10 et 99, et ainsi de suite avec le pattern « *(?).* », puis avec le pattern « *(??).* », et ainsi de suite ; ou encore utiliser un pattern plus complexe qui laisse le choix du nombre de caractères en précisant qu’on ne veut que des numéros, mais je n’entrerai pas dans les détails ici.

Par exemple j’ai pu utiliser la commande find -name « *(??).* » -exec rm {}  \; pour supprimer mes fichiers doublons compris entre 10 et 99.

[Avancé] Comment appliquer récursivement une instruction à des fichiers sous Linux

recursion par sebr (cliquez pour afficher l'original)

recursion par sebr (cliquez pour afficher l'original)

Figurez vous qu’aujourd’hui même, on m’a transmis une archive contenant un meli-melo de fichiers .java et de fichiers .class avec pas mal de ramifications des dossiers. Or il se trouve que seuls les fichiers en .java m’intéressaient. Ne souhaitant pas passer des heures à pacourir les dossiers pour virer tous les .class manuellement, je me suis mis en quête d’une commande susceptible d’explorer récursivement les dossiers et sous dossiers, et d’en effacer tous les fichiers en .class.

Après quelques recherches dans les manpages, et quelques essais infructueux, j’ai posé la question sur irc (un salon de discussion en ligne), et j’ai immédiatement obtenu deux réponses toutes aussi fonctionnelles l’une que l’autre. Il suffisait de faire:

  • soit find . -name ‘*.class’ | xargs rm -f
  • soit find . -name ‘*.class’ -exec rm -v {} \;

Comme vous le voyez, les instructions sont composées de deux parties: la première, find . -name ‘*.class’, permet de rechercher récursivement tous les fichiers se terminant par ‘.class‘. La deuxième partie précise le traitement à appliquer aux fichiers. Les deux reviennent au même, mais la manière dont le résultat va être obtenu diffère.

Après discussion il est apparu que la première était la plus fonctionnelle au niveau de la rapidité d’exécution, car la commande rm est appelée une seule fois pour être appliquée d’un coup à toute la liste de fichiers,  tandis que la deuxième s’arrête à chaque fichier trouvé pour le supprimer avant de poursuivre la recherche, ce qui implique l’appel à la fonction rm autant de fois qu’il y a de fichiers, donc la création d’autant de processus, ce qui est couteux pour le système. Cependant la deuxième peut aussi avoir son intéret si un jour j’ai des dizaines de milliers de fichiers à supprimer, car il semblerait que le nombre d’éléments pouvant être passés dans un pipe (la barre verticale) soit limitée. Cela dit le débat reste ouvert car tous n’étaient pas d’accord sur la manière de procéder du Xargs. Voir la page de Wikipedia dédiée à Xargs (en) pour plus d’informations.

Pour conclure, ici on s’est intéressé à l’effaçage récursif d’un type donné de fichier, mais on remarquera aisément qu’en remplaçant rm par une autre commande, on peut généraliser ainsi l’application récursive d’une instruction à tous les dossiers et sous dossiers d’un répertoire.

note: ne pas faire attention aux options du rm. Je les ai reportées telles qu’on me les a données, mais il faut bien voir ce qu’elles veulent dire: le ‘-v’ de la deuxième commande, demande au rm de dire ce qu’il fait au fur et à mesure, et le ‘-f’ de la première commande demande d’ignorer les éventuels avertissements du système. Il convient de les modifier avec discernement en fonction de la situation.

PS: Faites des sauvegardes avant de faire des tests :).