web-dev-qa-db-fra.com

Renommer les fichiers à mettre en majuscules sans impact sur les extensions de fichiers

J'utilise ceci sur le répertoire d'un client pour renommer des fichiers avec la première lettre de chaque mot en majuscule selon leur demande:

rename 's/\b(\w)/\u$1/g' *

Cela fonctionne très bien, mais cela met également la première lettre de l'extension en majuscule. J'utilise donc ceci pour résoudre ce problème:

rename 's/\.Txt$/.txt/' *.Txt

ce qui fonctionne bien si la plupart des fichiers d’un dossier ont la même extension (généralement vrai), mais est pénible s’ils sont très mélangés.

Pour résoudre ce problème, j'ai créé un petit script qui ressemble à ceci (ne pas rire!):

#!/bin/bash
rename 's/\.Txt$/.txt/' *.Txt
rename 's/\.Doc$/.doc/' *.Doc
rename 's/\.Docx$/.docx/' *.Docx
...
rename 's/\.Xlsx$/.xlsx/' *.Xlsx

J'ignore les erreurs 'Impossible de renommer * .Txt * .txt: Aucun fichier ou répertoire de ce type', et si je trouve une extension manquante, j'ajoute simplement cette ligne à mon script.

Je ne pense pas que cela importera, mais les fichiers se trouvent sur un serveur de partage de fichiers Windows, mais j'y accède à l'aide d'Ubuntu 16.04LTS. Si cela est important, je pourrais d'abord les copier sur mon lecteur local, exécuter une commande dans Ubuntu, puis déplacer les fichiers vers l'arrière si nécessaire.

Existe-t-il un moyen de modifier la première commande de renommage pour ignorer les extensions et les laisser en minuscules? Puis-je exécuter la nouvelle commande dans un répertoire de niveau supérieur et la faire passer en revue tous les sous-répertoires?

7
Alan

Une solution consiste à utiliser Negative Lookbehind pour ne faire correspondre que la séquence de caractères Word-borne - Word quand elle n'est pas précédée d'une période littérale, par exemple. donné

$ ls
alan's file.txt  bar.txt  foo

ensuite

$ rename -n 's/(?<!\.)\b\w*/\u$&/g' *
rename(alan's file.txt, Alan'S File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)

En supposant que vous souhaitiez également éviter de capitaliser la pluralisation s, nous pourrions également modifier cela.

$ rename -n 's/(?<![.'\''])\b\w*/\u$&/g' *
rename(alan's file.txt, Alan's File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)

ou même

$ rename -n 's/(?<![[:punct:]])\b\w*/\u$&/g' *
rename(alan's file.txt, Alan's File.txt)
rename(bar.txt, Bar.txt)
rename(foo, Foo)
7
steeldriver

Cela mettra en majuscule la première lettre de chaque mot sauf l'extension finale:

rename -n 's/\b(.+?)\b/\u$1/g; s/(.+)\.(.)/$1\.\l$2/' *

J'ai testé avec ces fichiers:

$ ls -1
'a file with a '$'\n''new line.foo'
'a file with spaces.txt'
justonelongfilename.ext
no-extensions-here
this.has.many.extensions.pdf

Et il les renommera en:

$ rename -n 's/\b(.+?)\b/\u$1/g; s/(.+)\.(.)/$1\.\l$2/' *
a file with a 
new line.foo -> A File With A 
New Line.foo
a file with spaces.txt -> A File With Spaces.txt
justonelongfilename.ext -> Justonelongfilename.ext
no-extensions-here -> No-Extensions-Here
this.has.many.extensions.pdf -> This.Has.Many.Extensions.pdf

L'astuce consiste à mettre d'abord en majuscule chaque première lettre, en ignorant les extensions, puis à revenir en arrière et à rendre l'extension en minuscule:

  • s/\b(.+?)\b/\u$1/g;: Le .+? est un motif non glouton, ce qui signifie qu'il trouvera la correspondance la plus courte possible. Comme il est ancré par des limites de mots (\b), tous les mots seront trouvés (tous à cause de la g finale). Celles-ci sont ensuite remplacées par la version majuscule (première lettre majuscule) d’eux-mêmes (\l$1).

  • s/(.+)\.(.)/$1\.\l$2/: le .+ est gourmand et trouvera la correspondance la plus longue possible. Cela signifie la chaîne la plus longue jusqu'à un . final, qui sera l'extension (le cas échéant). Nous remplaçons la correspondance par tout ce qui précède l'extension ($1), un . et l'extension avec la première lettre inférieure en majuscule (\l$2).


La récursion est assez facile aussi. Si votre shell est bash (si vous ne le savez pas, c'est probablement le cas), vous pouvez utiliser l'option globstar qui fait que ** correspond à 0 ou plusieurs sous-répertoires:

shopt -s globstar

Maintenant, lancez la commande rename comme ceci (cela fonctionnera également sur tous les fichiers de votre répertoire actuel):

rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' **

Pour le limiter aux seuls fichiers ou répertoires avec des extensions, utilisez **/*.* au lieu de **.

Sinon, utilisez find:

find /path/to/dir -exec rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' {} +

Et, pour limiter à ces fichiers et répertoires avec une extension:

find /path/to/dir -name '*.*' -exec rename -n 's/\b(.+?)\b/\u$1/g; s/(.*)\.(.)/$1\.\l$2/' {} +

Notez que toutes ces solutions renommeront volontiers les répertoires et les fichiers. Si vous ne le souhaitez pas, soyez prudent lorsque vous lui indiquez de recurse ou donnez-lui un modèle plus spécifique tel que *.txt.


Dans tous les exemples, supprimez le -n pour qu'ils fassent réellement quelque chose. Le -n provoque rename simplement imprimer ce qu’il ferait et ne rien faire. Utile pour les tests.

5
terdon

Le moyen le plus judicieux serait de s’attaquer à tous les "mots" (avec l’utilisation de la limite de mot \b et en faisant référence via $1 à un premier caractère), extension comprise, mais en minuscule l’extension elle-même:

$ prename -nv 's/\b(.)/\u$1/g;s/^(.*)\.(.*)/$1.\l$2/' *                                                                                                                
another filename trispaced.txt renamed as Another Filename Trispaced.txt
filename_with_underschore.txt renamed as Filename_with_underschore.txt
one filename.txt renamed as One Filename.txt

Notez que cela ne fonctionne pas pour le nom de fichier avec des traits de soulignement, c'est-à-dire que les limites "Word" sont considérées comme des blancs (onglets, espaces, nouvelles lignes - ce qui, espérons-le, ne devrait pas être dans le nom de fichier).

Notez l'utilisation de prename pour la portabilité vers ksh, où rename est en réalité une commande intégrée, et non l'exécutable autonome Perl.

4

Il suffit d'utiliser rename 's/\b(\w)/\u$1/' * SANS le drapeau g.
Le drapeau g signifie "global = faites-le plusieurs fois". Vous faites 1ère fois en nom de fichier, et vous ne voulez pas mettre en majuscule dans la 2ème fois non?

1
V-Mark