86 votes

Comment changer l'extension de plusieurs fichiers de manière récursive depuis la ligne de commande ?

J'ai de nombreux fichiers avec .abc et je veux les changer en .edefg
Comment faire cela en ligne de commande ?

J'ai un dossier racine avec de nombreux sous-dossiers, la solution devrait donc fonctionner de manière récursive.

107voto

Steve Karg Points 11

Une méthode portable (qui fonctionnera sur tout système compatible POSIX) :

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

En bash4, vous pouvez utiliser globstar pour obtenir des globs récursifs (**) :

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

Le (perl) rename dans Ubuntu peut renommer des fichiers en utilisant la syntaxe des expressions régulières perl, que vous pouvez combiner avec globstar ou find :

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

Voir aussi http://mywiki.wooledge.org/BashFAQ/030

53voto

Chakra Points 3322

Cela fera la tâche requise si tous les fichiers sont dans le même dossier.

rename 's/.abc$/.edefg/' *.abc

Pour renommer les fichiers de manière récursive, utilisez ceci :

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'

14voto

Christian Long Points 3167

Un problème avec les renommages récursifs est que, quelle que soit la méthode utilisée pour localiser les fichiers, elle transmet l'ensemble du chemin d'accès à l'application rename et pas seulement le nom du fichier. Il est donc difficile d'effectuer des renommages complexes dans des dossiers imbriqués.

J'utilise find 's -execdir action pour résoudre ce problème. Si vous utilisez -execdir au lieu de -exec la commande spécifiée est exécutée à partir du sous-répertoire contenant le fichier correspondant. Ainsi, au lieu de passer le chemin complet à rename il ne fait que passer ./filename . Cela rend l'écriture de la regex beaucoup plus facile.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;

En détail :

  • -type f signifie rechercher uniquement les fichiers, pas les répertoires
  • -name '*.abc' signifie que seuls les noms de fichiers se terminant par .abc sont pris en compte.
  • '{}' est l'espace réservé qui marque l'endroit où -execdir insérera le chemin trouvé. Les guillemets simples sont nécessaires, pour lui permettre de gérer les noms de fichiers avec des espaces et des caractères Shell.
  • Les barres obliques inversées après -type et -name sont le caractère de continuation de ligne de bash. Je les utilise pour rendre cet exemple plus lisible, mais ils ne sont pas nécessaires si vous mettez votre commande sur une seule ligne.
  • Toutefois, la barre oblique inversée à la fin de l'élément -execdir est nécessaire. Elle est là pour échapper au point-virgule, qui termine la commande exécutée par -execdir . Fun !

Explication de la regex :

  • s/ début de la regex

  • \.\/ correspond au ./ de tête que -execdir passe dans. Utilisez \ pour échapper aux métacaractères . et / (note : cette partie varie en fonction de votre version de find . Voir le commentaire de l'utilisateur @apollo)

  • (.+) correspond au nom du fichier. Les parenthèses capturent la correspondance pour une utilisation ultérieure.

  • \.abc échapper au point, correspondre à l'abc

  • $ ancrer la correspondance à la fin de la chaîne de caractères

  • / marque la fin de la partie "match" de la regex, et le début de la partie "replace".

  • version1_ ajouter ce texte à chaque nom de fichier

  • $1 fait référence au nom de fichier existant, car nous l'avons capturé avec des parenthèses. Si vous utilisez plusieurs jeux de parenthèses dans la partie "match", vous pouvez y faire référence ici en utilisant $2, $3, etc.

  • .abc le nouveau nom de fichier se terminera par .abc. Il n'est pas nécessaire d'échapper le métacaractère point ici dans la section "replace".

  • / fin de la regex

Avant

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

Après

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

Un conseil : rename L'option -n de 's est utile. Elle fait un essai et vous montre les noms qu'elle va changer, mais ne fait aucun changement.

5voto

Jonathan Dagle Points 168

Un autre moyen portable :

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;

4voto

ashanrupasinghe Points 141
# Rename all *.txt to *.text
for f in *.txt; do 
mv -- "$f" "${f%.txt}.text"
done

Voir également l'entrée sur la raison pour laquelle vous ne devriez pas analyser ls .

Edit : si vous devez utiliser basename votre syntaxe serait :

for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done

https://unix.stackexchange.com/questions/19654/changing-extension-to-multiple-files

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X