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

61

J'ai beaucoup de fichiers avec l'extension .abc et je veux les changer en .edefg
Comment faire cela depuis la ligne de commande?

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

    
posée tommyk 19.04.2011 - 14:02
la source

8 réponses

75

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

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

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

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

La commande (perl) rename d'Ubuntu peut renommer des fichiers en utilisant 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 lien

    
réponse donnée geirha 19.04.2011 - 20:51
la source
37

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/'
    
réponse donnée Chakra 19.04.2011 - 14:23
la source
11

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

J'utilise l’action find de -execdir 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. Donc, au lieu de passer le chemin entier à rename , il ne transmet que ./filename . Cela facilite grandement l’écriture de la regex.

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

En détail:

  • -type f signifie seulement rechercher les fichiers, pas les répertoires
  • -name '*.abc' signifie que seuls les noms de fichiers qui se terminent par .abc correspondent à
  • Les barres obliques inverses après -type et -name sont le 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.
  • Cependant, la barre oblique inverse à la fin de la ligne -execdir est requise. Il est là pour esacpe le point-virgule, qui termine la commande exécutée par -execdir . Fun!

Explication de l'expression rationnelle:

  • s/ start du regex
  • \.\/ correspond au premier ./ que -execdir passe. Utilisez \ pour échapper au. et / metacharacters
  • (.+) correspond au nom du fichier. Les parenthèses capturent la correspondance pour une utilisation ultérieure
  • \.abc échappe au point, correspond à l'abc
  • $ ancre la correspondance à la fin de la chaîne

  • / marque la fin de la partie "match" de l'expression rationnelle et le début de la partie "replace"

  • version1_ ajoute ce texte à chaque nom de fichier

  • référence le 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 vous y référer en utilisant $ 2, $ 3, etc.
  • .abc le nouveau nom de fichier se terminera par .abc. Nul besoin d'échapper au métacaractère point dans la section "remplacer"
  • / end du 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

Indice: L'option -n de rename est utile. Il fait un essai à sec et vous indique quels noms il va changer, mais ne fait aucun changement.

    
réponse donnée Christian Long 06.10.2012 - 00:48
la source
5

Une autre manière portable:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "" "$(dirname "")/$(basename "" .abc).edefg"' _ '{}' \;
    
réponse donnée aNeutrino 03.02.2013 - 17:36
la source
0
# Rename all *.txt to *.text
for f in *.txt; do 
mv -- "$f" "${f%.txt}.text"
done

Voir également l’entrée expliquant pourquoi vous ne devez pas analyser ls .

Edit: si vous devez utiliser basename, votre syntaxe serait:

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

lien

    
réponse donnée ashanrupasinghe 22.08.2016 - 14:12
la source
0

C’est ce que j’ai fait et ça a fonctionné comme je le voulais. J'ai utilisé la commande mv . J'avais plusieurs fichiers .3gp et je voulais tous les renommer en .mp4

Voici un petit résumé:

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

Qui analyse simplement le répertoire en cours, récupère tous les fichiers .3gp, puis renomme (en utilisant le mv) dans ren-name_of_file.mp4

    
réponse donnée Rexford 13.03.2016 - 22:58
la source
0

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
    
réponse donnée Peaceful 27.09.2017 - 05:39
la source
0

J'utilise la commande mmv à partir du package 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'
    
réponse donnée MvG 20.05.2018 - 23:13
la source

Lire d'autres questions sur les étiquettes