web-dev-qa-db-fra.com

Convertir toutes les extensions de fichiers en minuscules

J'essaie de mettre toutes mes extensions en minuscule, peu importe ce que c'est. Jusqu'à présent, d'après ce que j'ai vu, vous devez spécifier quelles extensions de fichier vous souhaitez convertir en minuscules. Cependant, je veux juste tout mettre en minuscule après le premier dernier point . dans le nom.

Comment puis-je faire cela dans bash?

34
thevoipman

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 s’appliquera aux noms de fichiers contenant des nouvelles lignes. Mais supporte-moi pour l'instant.

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 en cours (.) qui ont la période . dans leur 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 (cela 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 la variable a.

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

Le nom du fichier en cours de traitement ({}) est passé à sh -c en tant qu'argument supplémentaire et apparaît à l'intérieur de la ligne de commande sous la forme $0. Le script est sécurisé car, dans ce cas, le shell utilise {} comme donnée, pas comme une partie de code, comme quand il est spécifié 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, quelque chose comme:

; rm -rf * ;

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

While-version

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

find . -name '*.*' | while IFS= read -r f
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 disposer d'une find prenant en charge -print0 (comme GNU find) et de Bash (afin que read prenne en charge le commutateur de délimiteur -d):

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

Cela se produit toujours pour les fichiers contenant des nouvelles lignes (car ils seront absorbés par le sous-shell a=$(...). Si vous vraiment voulez une méthode infaillible (et vous devriez!), Avec une version récente de Bash (Bash≥4.0) supporte l'expansion des paramètres ,, 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 un find aller (retour à la solution originale avec quelques corrections 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 afin que seuls les fichiers normaux soient renommés. Sans cela, vous pourriez toujours avoir des problèmes si les noms de répertoire sont renommés avant les noms de fichier. 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 find effectue une recherche en profondeur d'abord.

Vous pouvez faire valoir 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.

61
Igor Chubin

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

rename JPG jpg *.JPG

rename est une commande qui demande au shell de renommer chaque occurrence de JPG en jpg dans le dossier actuel avec tous les noms de fichiers ayant l'extension JPG.

Si vous voyez que les mots clés "JPG" ne sont pas autorisés alors que "subs strict" est utilisé à la première ligne (eval 1) avec cette approche, essayez:

rename 's/\.JPG$/.jpg/' *.JPG
47
P Sreedhar

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

Ensuite, il vous suffit de créer une liste des fichiers que vous devez renommer en son 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 ...

10
thkala

Cela fera le travail de votre '.mp3 - mais seulement dans le répertoire de travail - mais est capable de consommer 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
3
Theodros Zelleke

Si vous avez installé mmv (= déplacer plusieurs fichiers) et que vos noms de fichier 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 pour * n'est pas glouton dans 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 à retenir que tous les trucs find/exec/sed.

1
Elmar Zander

récursivement pour tous une solution fine:

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

Ce qui précède renomme récursivement les fichiers portant l’extension "JPG" en fichiers portant l’extension "jpg"

1
Govind Totla

Si vous utilisez ZSH:

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

Si vous obtenez zsh: command not found: zmv, lancez simplement:

autoload -U zmv

Et puis réessayez.

Merci à cet article article original pour l’information sur zmv et la documentation ZSH pour la syntaxe de substitution minuscule/majuscule.

1
Michael Leonard

Si vous avez installé JDK, vous pouvez simplement compiler les éléments suivants:

import Java.io.File;

public class FileRename {
    public static void main(String[] args) {
        long numFilesChanged = 0;
        long numFilesNotChanged = 0;

        try {
            final File directory = new File(".");
            final File[] listOfFiles = directory.listFiles();

            for (int i = 0; i < listOfFiles.length; i++) {
                if (!listOfFiles[i].isFile()) {
                    continue;
                }

                final File fileToRename = listOfFiles[i];

                final String filename = fileToRename.getName();
                final String woExtension = filename.substring(0, filename.lastIndexOf('.'));
                final String extension = filename.substring(filename.lastIndexOf('.'));

                if (extension.toLowerCase().equals(extension)) {
                    numFilesNotChanged++;
                } else {
                    numFilesChanged++;
                    fileToRename.renameTo(new File(woExtension + extension.toLowerCase()));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("Changed: " + numFilesChanged);
        System.out.println("Not changed:" + numFilesNotChanged);
    }
}

et courir. 

Si vous n'avez pas installé JDK mais Java, compilez-le à l'aide d'un compilateur Java en ligne tel que: https://www.compilejava.net/ et placez simplement le fichier .class obtenu dans le dossier où vous voulez renommez vos fichiers et exécutez: Java FileRename.

N'hésitez pas à me contacter si vous n'êtes pas familier avec Java ou si vous avez besoin d'aide pour la compilation et/ou l'exécution.

0
Koray Tugay

Si vous ne vous intéressez qu'à certaines extensions de fichier, telles que la conversion de toutes les extensions "JPG" en majuscules en minuscules "jpg", vous pouvez utiliser l'utilitaire de ligne de commande rename like. CD dans le répertoire que vous souhaitez modifier. ensuite

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

Utilisez l'option -n pour tester ce qui va changer, puis utilisez-le sans résultat

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