Convertir toutes les extensions de fichiers en minuscules

J’essaie de minimiser toutes mes extensions indépendamment de ce que c’est. Jusqu’à présent, d’après ce que j’ai vu, vous devez spécifier les extensions de fichiers que vous souhaitez convertir en minuscules. Cependant, je veux juste tout réduire après le premier point . dans le nom.

Comment puis-je faire ça en bash ?

Solution

Vous pouvez résoudre la tâche en une seule ligne:

 find . -name '*.*' -exec sh -c ' a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \; 

Remarque: cela va casser les noms de fichiers contenant des lignes nouvelles. Mais rest avec moi pour le moment.

Exemple d’utilisation

 $ mkdir C; touch 1.TXT a.TXT B.TXT C/D.TXT $ find . . ./C ./C/D.TXT ./1.TXT ./a.TXT ./B.TXT $ find . -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \; $ find . . ./C ./C/D.txt ./a.txt ./B.txt ./1.txt 

Explication

Vous trouvez tous les fichiers dans le répertoire courant ( . ) Qui ont une période . dans son nom ( -name '*.*' ) et exécutez la commande pour chaque fichier:

 a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "{}" "$a" 

Cette commande signifie: essayez de convertir l’extension de fichier en minuscule (ce qui rend sed ):

 $ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/" 1.txt $ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/" 2.txt 

et enregistrez le résultat dans a variable.

Si quelque chose a été changé [ "$a" != "$0" ] , renommez le fichier mv "$0" "$a" .

Le nom du fichier en cours de traitement ( {} ) est passé à sh -c tant qu’argument supplémentaire et apparaît dans la ligne de commande sous la forme $0 . Cela rend le script sûr, car dans ce cas, le shell prend {} en tant que donnée, pas en tant que partie de code, comme si elle était spécifiée directement dans la ligne de commande. ( Je remercie @gniourf_gniourf de m’avoir signalé ce problème très important ).

Comme vous pouvez le voir, si vous utilisez directement {} dans le script, il est possible d’avoir des injections de shell dans les noms de fichiers, par exemple:

 ; rm -rf * ; 

Dans ce cas, l’injection sera considérée par le shell comme une partie du code et elles seront exécutées.

En version

Version plus claire, mais un peu plus longue du script:

 find . -name '*.*' | while IFS= read -rf do a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$f" ] && mv "$f" "$a" done 

Cela se casse toujours pour les noms de fichiers contenant des nouvelles lignes. Pour résoudre ce problème, vous devez avoir une find qui prend en charge -print0 (comme GNU find ) et Bash (afin que read -print0 charge le commutateur -d délimiteur):

 find . -name '*.*' -print0 | while IFS= read -r -d '' f do a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$f" ] && mv "$f" "$a" done 

Ceci est toujours valable pour les fichiers qui contiennent des nouvelles lignes (car ils seront absorbés par le sous-shell a a=$(...) . Si vous voulez vraiment une méthode infaillible (et vous devriez le faire!), Avec une version récente de Bash (Bash≥ 4.0) qui prend en charge le développement de parameters, voici la solution ultime:

 find . -name '*.*' -print0 | while IFS= read -r -d '' f do base=${f%.*} ext=${f##*.} a=$base.${ext,,} [ "$a" != "$f" ] && mv -- "$f" "$a" done 

Retour à la solution d’origine

Ou dans une find allez (retour à la solution originale avec quelques correctifs qui le rend vraiment infaillible):

 find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \; 

J’ai ajouté -type f pour que seuls les fichiers réguliers soient renommés. Sans cela, vous pourriez toujours avoir des problèmes si les noms de répertoires sont renommés avant les noms de fichiers. Si vous souhaitez également renommer des répertoires (et des liens, des tuyaux, etc.), vous devez utiliser -depth :

 find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \; 

de sorte que la find effectue une recherche en profondeur en premier.

Vous pouvez prétendre qu’il n’est pas efficace de générer un processus bash pour chaque fichier trouvé. C’est correct, et la version précédente de la boucle serait alors meilleure.

J’ai eu du succès avec cette commande.

 rename JPG jpg *.JPG 

rename est une commande qui dit au shell de renommer toutes les occurrences de JPG en jpg dans le dossier actuel avec tous les noms de fichiers ayant l’extension JPG .

Si vous voyez Bareword “JPG” non autorisé alors que “ssortingct subs” utilisé à (eval 1) ligne 1 avec cette approche, essayez:

 rename 's/\.JPG$/.jpg/' *.JPG 

Eh bien, vous pouvez utiliser cet extrait comme base de toute alternative dont vous avez besoin:

 #!/bin/bash # lowerext.sh while read f; do if [[ "$f" = *.* ]]; then # Extract the basename b="${f%.*}" # Extract the extension x="${f##*.}" # Convert the extension to lower case # Note: this only works in recent versions of Bash l="${x,,}" if [[ "$x" != "$l" ]]; then mv "$f" "$b.$l" fi else continue fi done 

Par la suite, tout ce que vous avez à faire est de fournir une liste des fichiers que vous devez renommer en entrée standard. Par exemple, pour tous les fichiers du répertoire en cours et de tout sous-répertoire:

 find -type f | lowerext.sh 

Une petite optimisation:

 find -type f -name '*.*' | lowerext.sh 

Vous devrez être plus précis si vous avez besoin d’une réponse plus concrète que celle-ci …

C’est plus court mais plus général, combiné de la réponse des autres:

 rename 's/\.([^.]+)$/.\L$1/' * 

Simulation

Pour la simulation, utilisez -n , autrement dit rename -n 's/\.([^.]+)$/.\L$1/' * . De cette façon, vous pouvez voir ce qui sera changé avant que les changements réels ne soient effectués. Exemple de sortie:

 Happy.Family.GATHERING.JPG renamed as Happy.Family.GATHERING.jpg Hero_from_The_Land_Across_the_River.JPG renamed as Hero_from_The_Land_Across_the_River.jpg rAnD0m.jPg1 renamed as rAnD0m.jpg1 

Brève explication sur la syntaxe

  • La syntaxe est rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
  • \.([^.]+)$ signifie la séquence de tout sauf le point ( [^.] ) à la fin de la chaîne ( $ ), après le point ( \. )
  • .\L$1 signifie point ( \. ) Suivi de minuscules ( \L ) du 1 er groupe ( $1 )
  • Le premier groupe dans ce cas est l’extension ( [^.]+ )
  • Vous feriez mieux d’utiliser le guillemet simple ' au lieu de guillemets doubles " pour envelopper la regex afin d’éviter une extension du shell

Cela fera le travail pour vos ‘.mp3’ – mais seulement dans le répertoire de travail – mais est capable de consumr des noms de fichiers avec des espaces:

 for f in *.[mM][pP]3; do mv "$f" "${f%.*}.mp3"; done 

Correction:

 for f in *.[mM][pP]3; do [[ "$f" =~ \.mp3$ ]] || mv "$f" "${f%.*}.mp3"; done 

Si vous avez mmv (= déplacer plusieurs fichiers) et que vos noms de fichiers contiennent au plus un point, vous pouvez utiliser

 mmv -v "*.*" "#1.#l2" 

Il n’a pas plus d’un point à droite (puisque l’algo correspondant à * n’est pas gourmand en mmv ), cependant, il gère correctement () et ' . Exemple:

 $ mmv -v "*.*" "#1.#l2" FOO.BAR.MP3 -> FOO.bar.mp3 : done foo bar 'baz' (CD 1).MP3 -> foo bar 'baz' (CD 1).mp3 : done 

Pas parfait, mais beaucoup plus facile à utiliser et à mémoriser que tous les trucs find/exec/sed .

récursivement pour toute une solution fine:

 find -name '*.JPG' | sed 's/\(.*\)\.JPG/mv "\1.JPG" "\1.jpg"/' |sh 

Le ci-dessus renomme récursivement les fichiers avec l’extension “JPG” en fichiers avec l’extension “jpg”

Si vous utilisez ZSH:

 zmv '(*).(*)' '$1.$2:l' 

Si vous obtenez zsh: command not found: zmv alors simplement:

 autoload -U zmv 

Et puis réessayez.

Merci à cet article original pour la pointe sur zmv et la documentation ZSH pour la syntaxe de substitution en minuscules / majuscules.

Donc, ces solutions qui ressemblent à du bruit de ligne sont sympas et tout, mais c’est facile à faire à partir de python REPL (je sais que l’OP a demandé bash, mais python est installé sur beaucoup de systèmes qui ont bash ces jours … ):

 import os files = os.listdir('.') for f in files: path, ext = os.path.splitext(f) if ext.isupper(): os.rename(f, path + ext.lower()) 

Si vous êtes seulement intéressé par certaines extensions de fichiers comme la conversion de toutes les extensions majuscules “JPG” en minuscules “jpg” Vous pouvez utiliser l’utilitaire de ligne de commande renommé comme ça. CD dans le répertoire que vous souhaitez modifier. alors

 rename -n 's/\.JPG$/\.jpg/' * 

Utilisez l’option -n pour tester ce qui sera changé, puis quand vous êtes satisfait des résultats, utilisez sans

 rename 's/\.JPG$/\.jpg/' *