J'ai beaucoup de fichiers avec l' .abc
extension 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.
J'ai beaucoup de fichiers avec l' .abc
extension 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.
Réponses:
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 rename
commande (perl) d’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/030
${1%.abc}...
sh -c
le premier argument, il est attribué à $ 0 et tous les arguments restants sont attribués aux paramètres de position . Donc, l' _
argument est factice, et '{}' est le premier argument de position sh -c
.
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/'
rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abc
dans une version moderne de Bash.
s
au début et quelles autres options puis-je utiliser là-bas? Merci !
Un problème avec les renommements récursifs est que quelle que soit la méthode utilisée pour localiser les fichiers, elle transmet le chemin complet rename
, et pas seulement le nom du fichier. Cela rend difficile de renommer des dossiers complexes dans des dossiers imbriqués.
J'utilise find
l' -execdir
action pour résoudre ce problème. Si vous utilisez -execdir
plutôt que 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 par tout le chemin rename
, il ne fait que passer ./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 ne correspondent qu'aux noms de fichiers se terminant par .abc'{}'
est l'espace réservé qui marque l'endroit où -execdir
sera inséré le chemin trouvé. Les guillemets simples sont obligatoires pour lui permettre de gérer les noms de fichiers avec des espaces et des caractères shell.-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 si vous placez votre commande sur une seule ligne.-execdir
ligne est requise. Il est là pour échapper au point-virgule, qui termine la commande exécutée par -execdir
. Amusement!Explication de la regex:
s/
début de la regex\.\/
correspond au premier ./ que -execdir transmet. Utilisez \ pour sortir du fichier. et / metacharacters (note: cette partie varie en fonction de votre version de find
. Voir le commentaire de l'utilisateur @apollo)(.+)
correspondre au nom de fichier. Les parenthèses capturent la correspondance pour une utilisation ultérieure\.abc
échapper au point, correspondre à l'abc$
ancrer l'allumette à 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 "remplacer"
version1_
ajouter ce texte à chaque nom de fichier
$1
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 "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 la regexAvant
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: rename
l'option 's -n est utile. Il effectue un essai et vous indique les noms qu'il va changer, mais ne fait aucun changement.
--execdir
n'est pas passé au premier plan ./
pour que la \.\/
partie dans la regex puisse être omise. Mai, c'est parce que je suis sur OSX pas ubuntu
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 pourquoi 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
C'est ce que j'ai fait et travaillé exactement comme je le voulais. J'ai utilisé la mv
commande. J'avais plusieurs .3gp
fichiers et je voulais tous les renommer.mp4
Voici un bref résumé pour cela:
for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done
Qui scanne simplement à travers le répertoire courant, ramasse tous les fichiers .3gp, puis les renomme (en utilisant le mv) en ren-name_of_file.mp4
file.3gp
à file.3gp.mp4
, qui pourrait ne pas être ce que la plupart des gens veulent.
.3gp
ou .mp4
ici étaient juste à des fins d'illustration.
basename "$i" .mp4
l'extension précédente au lieu de "ren- $ i.mp4".
J'utiliserais la commande mmv du paquetage du même nom :
mmv ';*.abc' '#1#2.edefg'
Les ;
correspondances 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' -execdir
option awesome fait un cd
dans le répertoire avant d'exécuter la rename
commande, contrairement à -exec
.
-depth
Assurez-vous que le changement de nom a lieu en premier lieu sur les enfants, puis sur les parents, afin d'éviter d'éventuels problèmes avec les répertoires parents manquants.
-execdir
est obligatoire 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 ont échoué:
rename 's/findme/replaceme/g' acc/acc
Le PATH
piratage est nécessaire car -execdir
présente un inconvénient très gênant: il find
est extrêmement avisé et refuse de faire quoi que ce soit avec -execdir
si vous avez des chemins relatifs dans votre PATH
variable d’environnement, par exemple ./node_modules/.bin
, en é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 utiliser l'action '-execdir' n'est pas sécurisé pour le répertoire qui se trouve dans le PATH?
-execdir
est une extension de recherche GNU à POSIX . rename
est basé sur Perl et provient du rename
package. Testé sous Ubuntu 18.10.
Renommer la solution de contournement de lookahead
Si vos chemins d'entrée ne proviennent pas find
ou si vous en avez assez de la gêne liée au chemin relatif, 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-to-file-path-before-executing/915686
Il sort -r
est nécessaire de s'assurer que les fichiers viennent après leurs répertoires respectifs, car les chemins les plus longs viennent après les plus courts avec le même préfixe.