web-dev-qa-db-fra.com

Copier récursivement (et renommer) des fichiers dans leur propre répertoire avec un autre nom

Cette Q & A couvrira un cas très spécifique et pourtant courant: étant donné que de nombreux fichiers portant le même nom ou un nom similaire chacun dans son propre répertoire, créez une copie de chacun d’entre eux dans leur répertoire d’origine mais avec un autre nom.

Vous pourriez vous demander quel cas rare nécessiterait une telle opération. Ce n'est pas si rare. Certains logiciels émergents, en particulier ceux basés sur le modèle de conception MVC (Modèle/Vue/Contrôleur) peuvent nécessiter que le programmeur implémente les divers composants, chacun dans un répertoire différent.

Exemple:

software root
|
|-- Model => MyCustomComponent.php
|
|-- View => MyCustomComponent.php
|
|-- Controller => MyCustomComponent.php
|
\-- Translations
         |
         |-- English => MyCustomComponent.php
         |
         |-- French => MyCustomComponent.php
         |
         |-- Italian => MyCustomComponent.php
         |
         |-- Spanish => MyCustomComponent.php
         |

L’application de commerce électronique très répandue OpenCart est un exemple de logiciel assez connu mettant en œuvre une telle structure. Mais il y en a beaucoup d'autres.

Une pratique courante consiste à créer de nouveaux composants en copiant un composant existant ou un composant principal, à appliquer des variantes et à les enregistrer. Au cas où l'héritage et autres ne peuvent pas nous aider, nous devons dupliquer 10 à 20 fichiers. Un processus totalement ennuyeux et sujet aux erreurs.

Voici un exemple typique: à partir d’un script de virement bancaire, implémentez un script de prélèvement automatique en utilisant l’ancien en tant que "code standard" (ne me dites pas que je n’ai pas créé cette architecture). Entre les traductions, la gestion des modèles, des vues et des contrôleurs, entre autres, le nombre de fichiers à copier dans leur répertoire complet devient rapidement lourd.

Lots of same name files, one per directory

2
Dario Fumagalli

Ce petit script vient aider exactement dans cette situation.

Avec un répertoire de départ, un motif de nom de fichier, une partie du nom de fichier d'origine à modifier et la partie du nom de fichier de destination à utiliser, il va:

  • Copier en profondeur tous les fichiers correspondants en préservant les attributs par rapport aux nouveaux (copie)

ou

  • Deep renommer tous les fichiers correspondants aux nouveaux noms (renommer)

ou

  • Il suffit d'afficher une liste de prévisualisation des fichiers potentiellement affectés (prévisualisation).

La dernière option est particulièrement utile car la sélection de la correspondance appropriée couvrant une masse de fichiers avec des variantes de noms ou d'extensions peut rapidement devenir un défi. Évitez les dégâts en prévisualisant les modifications avant de les appliquer!

Le script est très simple et a NO prétexte d'être le meilleur, le plus rapide, le plus propre, le plus sûr. C'est ce qui fonctionne pour moi et j'ai décidé de le partager dans l'espoir que quelqu'un d'autre en profitera.

Si vous le démarrez sans paramètres ou avec une commande incorrecte, il affichera de brèves instructions:

./copy_multi_files.sh

Usage:
./copy_multi_files.sh command path name_pattern src dest
If command is 'copy' then the script copies src to dest
If command is 'rename' then the script renames src to dest
If command is 'preview' then the script just shows the matched files
Inside path, for every file name matching name_pattern, it copies every src file to dest file


Paramètres:

  • Commande: il peut s'agir de copy, rename ou preview. Copy effectue la copie profonde récursive sur les nouveaux fichiers, renomme simplement les fichiers existants renommés en masse, la prévisualisation indique uniquement les fichiers qui seront affectés et comment.

  • Chemin: c'est le répertoire de départ. Je suggère de faire des expériences sur de petits répertoires afin d’avoir confiance dans le script avant d’essayer votre système de fichiers de 4 péta-trillions.

  • Name_pattern: cela détermine quels fichiers seront affectés, il est donc important de choisir une valeur raisonnable. Par exemple, si je voulais sélectionner un grand nombre de scripts dont le nom commence par "sendmail", je pourrais spécifier s* pour ce paramètre. Pour ceux qui connaissent les commandes Shell, ce modèle est passé en interne à find.

  • src: ceci est la partie, un modèle de nom de fichier à remplacer par dest lors de la formation des noms de fichiers copiés.

  • dest: c'est ce qui remplace src dans les nouveaux noms de fichiers copiés.

Un peu de vérification de l'intégrité est effectuée afin que name_pattern et src produisent des résultats cohérents. En d'autres termes, seuls les fichiers correspondant à name_pattern et contenant également src sont sélectionnés.

Déroutant? Ne vous inquiétez pas, j'ai préparé des exemples et des "pièges".


Simple, un exemple de dossier:

Nous avons des fichiers dans un sous-répertoire appelé: apps/res/

Nous voulons copier tous les fichiers dont le nom contient le mot: sendmail dans de nouveaux fichiers qui, à la place de sendmail ont newthing.

Voici une liste partielle du contenu de apps/res/:

robot.php
sendmail_test.php
sendmail_test2.php
sendmail_test3.php
showinfo.php
show_server_vars.php
version.php

Nous pouvons émettre la commande suivante:

./copy_multi_files.sh preview apps/res/ s* sendmail newthing

Cette ligne de commande nous dit que nous voulons juste un preview; le répertoire de départ est apps/res/ (c'est-à-dire qu'il se trouve dans notre répertoire actuel), nous allons rechercher tous les fichiers commençant par s (s*). Parmi les fichiers trouvés, nous allons choisir uniquement ceux dont le nom contient sendmail et les copier dans des fichiers résidant dans les mêmes répertoires mais avec sendmail remplacés par newthing.

Voici le résultat de cet aperçu:

apps/res/sendmail_test.php => apps/res/newthing_test.php
apps/res/sendmail_test2.php => apps/res/newthing_test2.php
apps/res/sendmail_test3.php => apps/res/newthing_test3.php

Vous voyez, même si nous spécifions s* comme motif de recherche, le script était suffisamment "intelligent" pour ne sélectionner que le sous-ensemble de fichiers "débutant par s" contenant également "sendmail".

Lançons la commande:

./copy_multi_files.sh copy apps/res/ s* sendmail newthing

ls apps/res/ montrera comment nous disposons maintenant des fichiers d'origine mais également des fichiers identiques avec newthing. Si nous utilisions "renommer" dans la ligne ci-dessus, nous n'aurions que les fichiers newthing.

newthing_test.php
newthing_test2.php
newthing_test3.php
robot.php
sendmail_test.php
sendmail_test2.php
sendmail_test3.php
showinfo.php
show_server_vars.php
version.php

Changeons les fichiers à nouveau. Cette fois, nous les avons renommés:

./copy_multi_files.sh rename apps/res/ new* new even_newer_

Notez les valeurs utilisées pour les différents paramètres. Le répertoire est maintenant le suivant:

even_newer_thing_test.php
even_newer_thing_test2.php
even_newer_thing_test3.php
robot.php
sendmail_test.php
sendmail_test2.php
sendmail_test3.php
showinfo.php
show_server_vars.php
version.php


Exemple plus complexe: passons au dossier d'installation principal d'OpenCart et revenons aux fichiers MVC du virement bancaire. Nous devons créer des copies de ces fichiers, chacun dans son répertoire respectif. Ils seront utilisés pour mettre en œuvre un module de paiement par prélèvement automatique. Voici la ligne de commande et un extrait du résultat:

./copy_multi_files.sh preview . ban* bank_transfer.* direct_debit

./catalog/view/language/russian/payment/bank_transfer.php => ./catalog/view/language/russian/payment/direct_debit
./catalog/view/language/czech/payment/bank_transfer.php => ./catalog/view/language/czech/payment/direct_debit
./catalog/view/theme/default/template/payment/bank_transfer.tpl => ./catalog/view/theme/default/template/payment/direct_debit


Oops! Quelque chose a mal tourné! Heureusement pour nous d'avoir utilisé l'aperçu. Quel est le problème dans la commande ci-dessus? L'argument "src" est le mauvais. En fait, bank_transfer.* est transmis en interne à une fonction directe "recherche et remplacement" (sed). Ce n'est pas développé avec une fonction "fichier globbing" mais comme une expression régulière. Par conséquent, tous les fichiers de l'aperçu sont transformés en: direct_debit.


Maintenant que nous sommes au courant de cette erreur, nous pouvons réécrire la ligne de commande pour obtenir le résultat souhaité:

/var/www/copy_multi_files.sh preview . ban* bank_transfer direct_debit

./catalog/view/language/russian/payment/bank_transfer.php => ./catalog/view/language/russian/payment/direct_debit.php
./catalog/view/language/czech/payment/bank_transfer.php => ./catalog/view/language/czech/payment/direct_debit.php
./catalog/view/theme/default/template/payment/bank_transfer.tpl => ./catalog/view/theme/default/template/payment/direct_debit.tpl

Maintenant ça marche! Maintenant, les extensions sont conservées, même lorsqu'elles changent (.php et .tpl).

Suit le petit code source du script. Je suggère fortement de sauvegarder les sauvegardes avant de renommer "live" et de toujours utiliser la fonctionnalité de prévisualisation. Rappelez-vous qu’il s’agit d’un script Unix récursif, ses effets peuvent être extrêmement méchants et irréversibles!

Code source:

#!/bin/sh

if [ "$#" -eq 0 ] || ( [ "$1" != "preview" ] && [ "$1" != "copy" ] && [ "$1" != "rename" ] )
        then
        echo "Usage:"
        echo "$0 command path name_pattern src dest"
        echo "If command is 'copy' then the script copies src to dest"
        echo "If command is 'rename' then the script renames src to dest"
        echo "If command is 'preview' then the script just shows the matched files"
        echo "Inside path, for every file name matching name_pattern, it copies every src file to dest file"
        echo
        exit 1
fi

for f in $(find "$2" -type f -name "$3" -name "*$4*" )
do
        z=`echo "$f" | sed s/"$4"/"$5"/`

        case "$1" in
                "preview") echo "$f => $z"
                ;;
                "copy") cp -p "$f" "$z"
                ;;
                "rename") mv "$f" "$z"
                ;;
        esac
done
1
Dario Fumagalli