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.
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
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/'
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-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.-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èreAvant
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.
Une autre façon portable:
find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;
# 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
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
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
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'
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.