Ich habe mit diesem Befehl Erfolg.
rename JPG jpg *.JPG
Wobei rename
ist ein Befehl, der die Shell anweist, jedes Vorkommen von JPG
umzubenennen bis jpg
im aktuellen Ordner mit allen Dateinamen mit der Erweiterung JPG
.
Wenn Sie sehen, dass Bareword "JPG" nicht erlaubt ist, während "strict subs" in (eval 1) Zeile 1 mit diesem Ansatz verwendet wird, versuchen Sie Folgendes:
rename 's/\.JPG$/.jpg/' *.JPG
Lösung
Sie können die Aufgabe in einer Zeile lösen:
find . -name '*.*' -exec sh -c '
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "$0" "$a" ' {} \;
Hinweis:Dies bricht bei Dateinamen, die Zeilenumbrüche enthalten. Aber ertragen Sie mich erst einmal.
Anwendungsbeispiel
$ 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
Erklärung
Sie finden alle Dateien im aktuellen Verzeichnis (.
) mit dem Punkt .
in seinem Namen (-name '*.*'
) und führen Sie den Befehl für jede Datei aus:
a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "{}" "$a"
Dieser Befehl bedeutet:Versuchen Sie, die Dateierweiterung in Kleinbuchstaben umzuwandeln (das macht sed
):
$ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/"
2.txt
und speichern Sie das Ergebnis in a
Variable.
Wenn etwas geändert wurde [ "$a" != "$0" ]
, benennen Sie die Datei um mv "$0" "$a"
.
Der Name der verarbeiteten Datei ({}
) an sh -c
übergeben als zusätzliches Argument und wird in der Befehlszeile als $0
angezeigt .Es macht das Skript sicher, denn in diesem Fall nimmt die Shell {} als Daten, nicht als Code-Teil, wie wenn es direkt in der Befehlszeile angegeben wird. (Danke @gniourf_gniourf für den Hinweis auf dieses wirklich wichtige Thema ).
Wie Sie sehen können, wenn Sie {}
verwenden Direkt im Skript ist es möglich, einige Shell-Injektionen in den Dateinamen zu haben, etwa so:
; rm -rf * ;
In diesem Fall wird die Injektion von der Shell als Teil des Codes betrachtet und ausgeführt.
While-Version
Klarere, aber etwas längere Version des Skripts:
find . -name '*.*' | while IFS= read -r f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Dies bricht immer noch für Dateinamen, die Zeilenumbrüche enthalten. Um dieses Problem zu beheben, benötigen Sie einen find
das -print0
unterstützt (wie GNU find
) und Bash (damit read
unterstützt den -d
Trennschalter):
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$f" ] && mv "$f" "$a"
done
Dies bricht immer noch für Dateien, die abschließende Zeilenumbrüche enthalten (da sie von a=$(...)
absorbiert werden Unterschale. Wenn Sie wirklich wollen eine narrensichere Methode (und das sollten Sie!), mit einer aktuellen Version von Bash (Bash≥4.0), die den ,,
unterstützt Parametererweiterung hier ist die ultimative Lösung:
find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
base=${f%.*}
ext=${f##*.}
a=$base.${ext,,}
[ "$a" != "$f" ] && mv -- "$f" "$a"
done
Zurück zur ursprünglichen Lösung
Oder in einem find
go (zurück zur ursprünglichen Lösung mit einigen Korrekturen, die es wirklich narrensicher machen):
find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
Ich habe -type f
hinzugefügt sodass nur normale Dateien umbenannt werden. Ohne dies könnten Sie immer noch Probleme haben, wenn Verzeichnisnamen vor Dateinamen umbenannt werden. Wenn Sie auch Verzeichnisse (und Links, Pipes usw.) umbenennen möchten, sollten Sie -depth
verwenden :
find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;
damit find
führt eine Tiefensuche durch.
Sie können argumentieren, dass es nicht effizient ist, einen bash
zu spawnen Prozess für jede gefundene Datei. Das ist richtig, und die vorherige Loop-Version wäre dann besser.
Dies ist kürzer, aber allgemeiner, kombiniert aus der Antwort anderer:
rename 's/\.([^.]+)$/.\L$1/' *
Simulation
Verwenden Sie für die Simulation -n
, also rename -n 's/\.([^.]+)$/.\L$1/' *
. Auf diese Weise können Sie sehen, was geändert wird, bevor die tatsächlichen Änderungen durchgeführt werden. Beispielausgabe:
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
Kurze Erklärung zur Syntax
- Die Syntax ist
rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
\.([^.]+)$
bedeutet eine Folge von allem außer Punkt ([^.]
) am Ende der Zeichenfolge ($
), nach dem Punkt (\.
).\L$1
bedeutet Punkt (\.
) gefolgt von Kleinbuchstaben (\L
) von 1 Gruppe ($1
)- Die erste Gruppe ist in diesem Fall die Erweiterung (
[^.]+
) - Verwenden Sie besser einfache Anführungszeichen
'
anstelle von doppelten Anführungszeichen"
um die Regex zu umschließen, um Shell-Expansion zu vermeiden