web-dev-qa-db-fra.com

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

J'ai beaucoup de fichiers avec l'extension .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 doit donc fonctionner de manière récursive.

71
tommyk

Un moyen portable (qui fonctionnera sur tout système compatible POSIX):

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

Dans 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

La commande (Perl) rename dans Ubuntu permet de renommer des fichiers à l’aide de la syntaxe d’expression régulière 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/0

84
geirha

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/'
44
Chakra

Un problème avec les renommements récursifs est que quelle que soit la méthode utilisée pour localiser les fichiers, le chemin d'accès complet est transféré à rename, pas seulement le nom du fichier. Cela rend difficile de renommer des dossiers complexes dans des dossiers imbriqués.

J'utilise l'action -execdir de find 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 transmettre le chemin complet à rename, il ne fait que transmettre ./filename. Cela facilite beaucoup l'écriture de la regex.

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

En détail:

  • -type f signifie seulement chercher des fichiers, pas des répertoires
  • -name '*.abc' signifie que seuls les noms de fichiers se terminant par .abc correspondent
  • Les barres obliques inverses après -type et -name correspondent au caractère de continuation de ligne bash. Je les utilise pour rendre cet exemple plus lisible, mais ils ne sont pas nécessaires dans la pratique.
  • Toutefois, la barre oblique inverse à la fin de la ligne -execdir est requise. C'est là pour échapper au point-virgule, qui termine la commande exécutée par -execdir. Amusement!

Explication de la regex:

  • s/ start of the regex
  • \.\/ correspond au premier ./ que -execdir transmet. Utilisez\pour échapper au. et/métacaractères
  • (.+) correspond au nom de fichier. Les parenthèses capturent la correspondance pour une utilisation ultérieure
  • \.abc échapper au point, correspondre à abc
  • $ ancre la correspondance à la fin de la chaîne

  • / marque la fin de la partie "match" de l'expression régulière, et le début de la partie "replace"

  • version1_ ajoute 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 "correspondance", vous pouvez vous y référer ici en utilisant $ 2, $ 3, etc.
  • .abc le nouveau nom de fichier se terminera par .abc. Pas besoin d'échapper au métacaractère de point ici dans la section "remplacer"
  • / fin de l'expression régulière

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

Astuce: L'option -n de rename 's est utile. Il effectue un essai et vous indique les noms qu'il va changer, mais ne fait aucun changement.

13
Christian Long

Une autre façon portable:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;
5
aNeutrino
# 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 le nom de base, votre syntaxe serait la suivante:

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

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

3
ashanrupasinghe

J'ai trouvé un moyen facile d'y parvenir. Pour changer les extensions de nombreux fichiers de jpg en pdf, utilisez:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done
0
Peaceful

C'est ce que j'ai fait et travaillé exactement comme je le voulais. J'ai utilisé la commande mv. J'ai eu plusieurs fichiers .3gp et je voulais tous les renommer en .mp4

Voici un bref résumé pour cela:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

Ce qui scanne simplement le répertoire en cours, récupère tous les fichiers .3gp, puis le renomme (à l’aide du fichier mv) en ren-name_of_file.mp4

0
Rexford

J'utiliserais le commande mmv du paquetage du même nom :

mmv ';*.abc' '#1#2.edefg'

Le ; correspond à zéro ou plus */ et correspond à #1 dans le remplacement. Le * correspond à #2. La version non récursive serait

mmv '*.abc' '#1.edefg'
0
MvG

Renommez les fichiers et les répertoires avec find -execdir | rename

Si vous voulez renommer les fichiers et les répertoires, pas simplement avec un suffixe, c'est un bon modèle:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find . -depth -execdir rename 's/findme/replaceme/' '{}' \;

L'option awesome -execdir insère un cd dans le répertoire avant d'exécuter la commande rename, contrairement à -exec.

-depth veille à ce que le changement de nom soit effectué en premier lieu sur les enfants, puis sur les parents, afin d'éviter d'éventuels problèmes avec des répertoires parent manquants.

-execdir est requis car le changement de nom ne fonctionne pas correctement avec les chemins d'entrée autres que les noms de base, par exemple. les échecs suivants:

rename 's/findme/replaceme/g' acc/acc

Le piratage PATH est requis car -execdir a un inconvénient très gênant: find est extrêmement avisé et refuse de faire quoi que ce soit avec -execdir si vous avez des chemins relatifs dans votre variable d'environnement PATH, par exemple. ./node_modules/.bin, échouant avec:

find: Le chemin relatif ‘./node_modules/.bin’ est inclus dans la variable d’environnement PATH, qui n’est pas sécurisée en combinaison avec l’action -execdir de find. Veuillez supprimer cette entrée de $ PATH

Voir aussi: Pourquoi l'utilisation de l'action '-execdir' n'est pas sûre pour un répertoire qui se trouve dans le PATH?

-execdir est un GNU find extension to POSIX . rename est basé sur Perl et provient du package rename. Testé sous Ubuntu 18.10.

Renommer la solution de contournement de lookahead

Si vos chemins d'entrée ne proviennent pas de find, ou si vous en avez assez du désagrément relatif des chemins, nous pouvons utiliser un préfixe Perl pour renommer en toute sécurité les répertoires comme suit:

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

Je n'ai pas trouvé d'analogue pratique pour -execdir avec xargs: https://superuser.com/questions/893890/xargs-change-working-directory-tocfile-path-before-executing/915686 =

Le sort -r est requis pour garantir que les fichiers viennent après leurs répertoires respectifs, car les chemins les plus longs suivent les plus courts avec le même préfixe.