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
Où 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
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
) [^.]+
) '
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/' *