[linux-l] Einfaches Shellscript zum Umbenennen vieler Dateien gesucht
Lutz Willek
lutz.willek at belug.de
Do Mai 16 08:16:37 CEST 2019
Hi Norman,
Danke, das ist eine wirklich sehr schöne Frage: genau richtig um sie
morgens beim ersten Kaffee zu beantworten.
Erst mal sehen ob ich Dich richtig verstanden habe. Ich baue Dein
Szenario mal nach.
Ausgangsstellung:
➜ ~ mkdir test
➜ ~ cd test
➜ for d in a b c d e f g ; do mkdir -p $d ; cd $d ; for f in $(seq 100
105) ; do touch ${f}_ ; done ; touch datei_ohne_am_ende ; cd .. ; done
➜ for f in $(seq 100 105) ; do touch ${f}_ ; done; touch
datei_ohne_am_ende;
Resultat: (Ausgabe gekürzt)
➜ find . -type f
./105_
./datei_ohne_am_ende
./g/105_
./g/datei_ohne_am_ende
./g/102_
./g/101_
./g/100_
./g/103_
./g/104_
./102_
./a/105_
./a/datei_ohne_am_ende
./a/102_
./a/101_
... (schnipp)...
Das entspricht in etwa Deiner jetzigen Situation? Wenn ja, dann würde ich:
- hier einfach mit einem "find" Befehl anfangen, um alle Dateien zu
filtern die mit einem Unterstrich enden (find . -type f -name '*_')
- dann die Ausgabe als Variable zeilenweise in eine Schleife legen
(nicht unbedingt nötig und unperformant, erleichtert aber meine
Erklärung ungemein)
- dann den Unterstrich entfernen und in eine zweite Variable packen
- dann einfach mit "mv" die Datei umbenennen.
Dieser Befehl sieht dann in etwa so aus: (hier wird nicht wirklich
umbenannt, sondern die Variablen werden nur angezeigt)
➜ find . -type f -name '*_' | while read file_ ; do file=$(echo $file_
| sed 's/_$//') ; echo Umbenennen von "$file_" nach "$file" ; done
Das Ergebnis (stark gekürzt) dürfte dann in etwa so aussehen:
Umbenennen von ./105_ nach ./105
Umbenennen von ./g/105_ nach ./g/105
Umbenennen von ./g/102_ nach ./g/102
Umbenennen von ./g/101_ nach ./g/101
Umbenennen von ./g/100_ nach ./g/100
... (schnipp)...
Falls Du die verwendeten Befehle nicht kennst und das Beispiel
nachvollziehen möchtest, dann schaue bitte in die jeweilig man page. Das
geht auf der Kommandozeile mit dem Befehl "man find" (hier um die manual
Seite für den Befehl "find" anzuzeigen), oder Du schaust einfach online
in den manpages nach:
http://man7.org/linux/man-pages/man1/find.1.html
http://man7.org/linux/man-pages/man1/bash.1.html
http://man7.org/linux/man-pages/man1/sed.1.html
http://man7.org/linux/man-pages/man1/echo.1.html
http://man7.org/linux/man-pages/man1/mv.1.html
Als sehr hilfreich empfinde ich übrigens die Webseite
https://explainshell.com/, da Du hier die Möglichkeit hast Dir alles auf
einen Blick anzusehen. Solltest Du diese Seite nicht kennen: Sie ist
meiner Meinung nach einen Blick wert. Der Beispielbefehl von oben wird
in dieser URL erklärt:
https://explainshell.com/explain?cmd=find+.+-type+f+-name+%27*_%27+%7C+while+read+file_+%3B+do+file%3D%24%28echo+%24file_+%7C+sed+%27s%2F_%24%2F%2F%27%29+%3B+echo+Umbenennen+von+%22%24file_%22+nach+%22%24file%22+%3B+done
Soweit die Theorie. In der Praxis würde ich den Befehl so nicht wirklich
verwenden, da er zwar alles schön erklärt, jedoch recht anfällig für
Fehler ist, und bei vielen 100.000 Dateien auch sehr sehr langsam. Aber
er erklärt schön das Grundprinzip von Linux-Befehlen: Einzelne kleine
und spezialisierte Befehle werden genutzt und dann mit einer Pipe (|)
kombiniert um eine Aufgabe zu erledigen.
In der Praxis würde ich beispielsweise noch dafür Sorge tragen das auch
Dateien mit Sonderzeichen korrekt behandelt werden, und auch etwas die
Performance optimieren indem ich einen speziellen Typ Schleife benutze,
der die paralelle Mehrfachausführung zulässt. Die Grundidee bleibt dabei
genau die gleiche, es ist jedoch nicht mehr ganz so einfach zu verstehen.
Um auch wirklich alle Sonderzeichen in den Dateinamen zu erwischen würde
ich wohl mit "find ... -print0" arbeiten.
Anstatt einer "while .. do .. done" Schleife würde ich in der Praxis
wohl eher mit einem "xargs" arbeiten. Als Alternative bietet sich auch
das "-exec" von find an.
Die Ersetzung mittels "sed" würde ich persönlich beibehalten, jedoch
könnte man hier auch andere Befehle wie awk oder inline shell
variablenersetzung verwenden.
Einschub: An dieser Stelle gibt es wirklich sehr viele Möglichkeiten zur
Optimierung, und ich bin mir ziemlich sicher das die "alten Hasen" hier
auf der Liste jetzt so ein zucken in den Fingern spüren, die Aufgabe
noch viel eleganter zu erledigen als ich. :-) --> Immer her mit
Alternativen! Eine bessere Steilvorlage bekommt ihr nie mehr!
Mein Endergebnis würde dann in etwa so aussehen: (dieser Befehl zeigt
wieder nur an anstatt wirklich Dateien umzubenennen)
➜ find . -type f -name '*_' -print0 | xargs --null -I {} sh -c "echo
Umbenennen von {} nach {}" | sed 's/_$//'
Das Ergebnis (stark gekürzt) ist identisch zur ersten Ausgabe:
Umbenennen von ./105_ nach ./105
Umbenennen von ./g/105_ nach ./g/105
Umbenennen von ./g/102_ nach ./g/102
Umbenennen von ./g/101_ nach ./g/101
Umbenennen von ./g/100_ nach ./g/100
... (schnipp)...
Um die Dateien dann auch wirklich umzubenennen wäre der endgültige
Befehl also: (Vorsicht: Dieser Befehl ändert wirklich Dateinamen!)
➜ find . -type f -name '*_' -print0 | xargs -0 -I {} echo mv "{}" "{}"
| sed 's/_$//' | sh
Siehe zur Erklärung:
https://explainshell.com/explain?cmd=+find+.+-type+f+-name+%27*_%27+-print0+%7C+xargs+-0+-I+%7B%7D+echo+mv+%22%7B%7D%22+%22%7B%7D%22+%7C+sed+%27s%2F_%24%2F%2F%27+%7C+sh
Das Endergebnis sieht dann so aus: (wieder stark gekürzt)
➜ find .
.
./104
./103
./102
./105
./datei_ohne_am_ende
./g
./g/104
./g/103
... (schnipp)...
Hoffe das hilft Dir weiter.
Liebe Grüße Lutz
On 16.05.19 01:02, Norman Steinbach wrote:
> Hallo Linux-Liste,
>
> ich suche ein Shellscript, welches viele Dateien in vielen
> Unterverzeichnissen rekursiv jeweils so umbenennt, dass das letzte
> Zeichen der Dateiendung gelöscht wird. Dieses Zeichen ist immer ein
> Unterstrich "_", und der soll weg.
>
> Hintergrund: Ich sichere gerade von einem übervollen Samsung-Handy
> Daten auf eine externe Festplatte. Das klappte per MTP nicht so, wie
> es sollte, weil jeweils zu lange gebraucht wird, um den
> Verzeichnisinhalt im Gerätespeicher einzulesen. Der Workaround ist
> gewesen, per "SmartSwitch" die Dateien (in erster Linie Tausende von
> Bildern) auf die SD-Karte zu sichern, von wo sie sich dann problemlos
> kopieren lassen.
> Diese Backup-Äpp benennt die Dateien jedoch immer so um, dass sie an
> den jeweiligen Dateinamen einen Unterstrich dran hängt.
>
> Da die Daten jedoch nicht als Backup in das Handy zurückgespielt
> werden sollen, sondern vom PC aus (unter Windows - das hat es
> überhaupt nicht hingekriegt mit der Sicherung) genutzt werden sollen,
> müssen die Dateinamen, um nun wieder "verwertbar" zu sein, alle wieder
> in ihr ordentliches Format gebracht werden, d.h. "dateiname.jpg_" muss
> wieder zu "dateiname.jpg" werden, bei allen anderen Endungen analog
> (z.B. ".mp4_" bei Videos zu ".mp4", oder ".pdf_" zu ".pdf" usw.)
>
> Hierfür gibt es sicherlich eine schöne, einfache Methode, das per
> Shell-Script zu automatisieren - nur dass ich leider nie gelernt habe,
> Shell-Scripte selbst zu entwickeln, weshalb ich hier um Hilfe bitten
> möchte.
>
> Falls das mit der Rekursion in die Unterverzeichnisse nicht
> funktioniert, wäre auch eine Variante "im aktuellen Verzeichnis"
> ausreichend, da nicht so viele davon relevante Daten enthalten, und
> der Rest eh wegfliegt.
>
>
> Danke & viele Grüße,
>
> Norman
> _______________________________________________
> linux-l mailing list
> linux-l at mlists.in-berlin.de
> Die Mailingliste der BeLUG (Berliner Linux User Group)
>
> Wenn du diese Mailingliste abbestellen willst, gehe bitte auf
> https://mlists.in-berlin.de/mailman/listinfo/linux-l-mlists.in-berlin.de
> und trage dich dort bitte aus
--
Lutz Willek <https://de.linkedin.com/in/lutzwillek/en?trk=profile-badge>
Mehr Informationen über die Mailingliste linux-l