web-dev-qa-db-fra.com

Copier les fichiers du même nom dans le même répertoire

J'ai un grand nombre de dossiers avec un fichier nommé genome.txt dont j'ai besoin pour cp dans le même dossier. J'essaie de comprendre comment faire pour que les fichiers ressemblent à genome1.txt, genome2.txt, etc. Je recherche une solution simple à cela.

4
JS Brewer

Script shell et son utilisation

Cela peut être fait avec un simple script Shell qui utilise la structure find ...|while read var ; do ... done (très courante dans le traitement des noms de fichiers). Le script bash ci-dessous fonctionne sur le répertoire actuel (en haut) et prend un seul argument pour la destination.

#!/bin/bash
find -name "genome.txt" -print0 | while IFS= read -r -d '' path
do
    base=$(basename --suffix=".txt"  "$path")
    new_path="${1%/}"/"$base"$counter".txt"
    echo "$path" "$new_path"
    counter=$(( $counter +1 ))
done

>>> NOTE <<< : le script utilise echo uniquement à des fins de test. Lorsque vous êtes satisfait des chemins résultants, remplacez echo par mv pour déplacer tous les noms de fichiers ou cp pour copier tous les noms de fichiers.

Exemple:

bash-4.3$ tree
.
├── destination
├── dir1
│   └── genome.txt
├── dir2
│   └── genome.txt
├── dir3
│   └── genome.txt
└── move_enumerated.sh

4 directories, 4 files
bash-4.3$ ./move_enumerated.sh  ./destination
./dir2/genome.txt ./destination/genome.txt
./dir3/genome.txt ./destination/genome1.txt
./dir1/genome.txt ./destination/genome2.txt

Améliorer le script pour plus de flexibilité

Ce script peut être encore amélioré pour le rendre plus général, où l'utilisateur peut spécifier le nom de fichier, le répertoire supérieur à parcourir et la destination en tant qu'arguments de ligne de commande:

#!/bin/bash
find "$2"  -name "$1" -print0 | while IFS= read -r -d '' path
do
    base=$(basename --suffix=".txt"  "$path")
    new_path="${3%/}"/"$base"$counter".txt"
    echo "$path" "$new_path"
    counter=$(( $counter +1 ))
done

Essai:

bash-4.3$ ./move_enumerated.sh "genome.txt"  "./testdir" "./testdir/destination"
./testdir/dir2/genome.txt ./testdir/destination/genome.txt
./testdir/dir3/genome.txt ./testdir/destination/genome1.txt
./testdir/dir1/genome.txt ./testdir/destination/genome2.txt

Syntaxe et théorie de fonctionnement

Dans l'ensemble, le script utilise la structure command | while read variable ; do ... done. C'est une approche très courante, et elle est fréquemment utilisée pour éviter de traiter avec ls et des noms de fichiers difficiles pouvant rompre les scripts.

Sur le côté gauche du tuyau, nous avons la commande find, qui prend le répertoire comme argument (et si ce n'est pas indiqué, GNU find utilisé dans Linux suppose que . - le répertoire de travail en cours). Les autres options incluent -name qui est le nom de fichier spécifique que nous recherchons et -print0 qui est utilisé pour afficher les résultats délimités via le caractère non imprimable \0. Il est fréquemment utilisé pour éviter de séparer les caractères de nouvelle ligne ou autres, car ces caractères peuvent potentiellement apparaître dans le nom du fichier même et par conséquent briser le script.

Sur le côté droit du tuyau, nous avons la structure while IFS= read -r -d '' ; do . . . done. La boucle while avec read Shell intégré est fréquemment utilisée pour la vraie entrée stdin, qui dans ce cas provient du tuyau. IFS=-r et -d '' sont nécessaires pour nous assurer de recevoir les noms de fichiers en toute sécurité et de reconnaître que chaque élément est délimité par \0.

Le reste du script est assez facile. Nous extrayons le nom de base du fichier en utilisant la commande basename. Étant donné que dans ce cas, nous traitons spécifiquement avec des extensions connues et attendons un seul point dans le nom du fichier, nous pouvons utiliser --suffix=".txt" pour supprimer cette partie, en laissant genome. Nous construisons ensuite un nouveau chemin d'accès au fichier via la destination de jonction, le nom de base et la variable compteur. Notez que nous utilisons le développement des paramètres avec l'argument "${3%/}" (dossier de destination) dans notre script amélioré. Ceci est fait pour s'assurer que, que l'utilisateur ajoute ou non le caractère / à la ligne de commande (./destination ou ./destination/), nous extrayons uniquement le nom de répertoire nu et nous le relions via / avec le nom de base. Notez également que la variable counter n’a pas été définie initialement. Le premier nom de fichier que nous recevons sera donc tout simplement genome.txt Après cela, la variable compteur sera incrémentée et ainsi créée et apparaîtra lorsque nous traiterons avec d’autres noms de fichiers.

Pour plus d'informations, veuillez lire Noms de fichier et noms de chemin dans Shell: comment le faire correctement .

4