Skript bzw. Befehl zum Ersetzen von Zeichen in Dateinamen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Skript bzw. Befehl zum Ersetzen von Zeichen in Dateiname

Beitrag von Meillo » 02.09.2016 23:45:38

Che hat geschrieben:Was hat das zu bedeuten? Wieso "über Gerätegrenzen hinweg",
/media und . werden auf verschiedenen Dateisystemen liegen. rename(1) scheint intern den Systemcall rename(2) zu verwenden. Der wirft aber in diesem Fall den Fehler EXDEV (siehe Manpage). Dieser fuehrt zu dieser Fehlermeldung. Wenn ueber Dateisysteme hinweg verschoben werden soll, dann muss kopiert und anschliessend die Quelle geloescht werden.


Vielleicht solltest du dir am einfachsten ein anderes Rename-Programm suchen, das auch ueber Dateisystemgrenzen hinweg verschieben kann.
Use ed once in a while!

Che
Beiträge: 358
Registriert: 09.06.2006 19:00:33

Re: Skript bzw. Befehl zum Ersetzen von Zeichen in Dateinamen

Beitrag von Che » 11.05.2019 03:03:27

Mal wieder bin ich dabei, Dateien- und Verzeichnisbezeichungen von unerwünschten Zeichen zu befreien. Schade, dass man sich aus Zeitmangel nicht wirklich tief einarbeiten kann.

Mit folgenden drei Befehlen, die großteils aus den Beiträgen von Euch stammen, konnte ich wirklich alles wie gewünscht ersetzen:

1. Muss mehrfach ausgeführt werden, bis alles ersetzt ist:

Code: Alles auswählen

 find ./ -execdir rename 's/ä/ae/;s/ö/oe/;s/ü/ue/;s/Ä/Ae/;s/Ö/Oe/;s/Ü/Ue/;s/ß/ss/;s/é/e/;s/ó/o/;s/í/i/;s/ú/u/;s/á/a/;s/ã/a/;s/Ê/E/;s/ê/e/;s/à/a/;s/è/e/;s/â/a/;s/õ/o/;s/ô/o/;s/Á/A/;s/Ò/O/;s/Ã/A/' -v {} \;
2. Eine Ausführung reicht. Der Befehl "1." soll unbedingt vorher erfolgreich angewandt worden sein, sonst werden z.B. Zeichen mit Umlaut hier durch "_" ersetzt:

Code: Alles auswählen

rename 's/[^[:alnum:].]/_/g' *
Nur schade, dass ich diesen Befehl bisher nicht zum rekursiven Verhalten überreden konnte, Grund warum ich ihn in jefem Unterverzeichnis extra ausführen muss. Kennt jemand eine Lösung dafür?
3. Verbleibende "." und mehrfach nebeneinander stehende "_" werden hiermit auf einem einzigen "_" versetzt:

Code: Alles auswählen

find ./ -execdir rename 's/\.\_/\_/;s/\_\_/\_/' -v {} \;
Noch offene Wüsche:

1. Jedem Befehl bei der Ausführung mitteilen, wie oft er ausgeführt werden soll. Vor allem beim 1. muss man mehrfach den Vorgang wiederholen. Da würde ich gerne ihm sagen können, mache jetzt 10 Durchläufe und dann höre auf.

2. Dann die drei Befehle in einer einzigen Zeile packen (mein Wunsch "1." berücksichtigt), sodass der Befehl-Bündel nur einmal ausgeführt werden müsste. Das würde sehr hilfreich sein :)

Viele Grüße
che
Zuletzt geändert von Che am 12.05.2019 01:36:18, insgesamt 1-mal geändert.

tobo
Beiträge: 1990
Registriert: 10.12.2008 10:51:41

Re: Skript bzw. Befehl zum Ersetzen von Zeichen in Dateinamen

Beitrag von tobo » 11.05.2019 16:20:10

Zunächst mal bin ich der Meinung, dass solche Endlos-Threads eigentlich keine gute Idee sind.

Che hat geschrieben: ↑ zum Beitrag ↑
11.05.2019 03:03:27
1. Muss mehrfach ausgeführt werden, bis alles ersetzt ist:

Code: Alles auswählen

 find ./ -execdir rename 's/ä/ae/;s/ö/oe/;s/ü/ue/;s/Ä/Ae/;s/Ö/Oe/;s/Ü/Ue/;s/ß/ss/;s/é/e/;s/ó/o/;s/í/i/;s/ú/u/;s/á/a/;s/ã/a/;s/Ê/E/;s/ê/e/;s/à/a/;s/è/e/;s/â/a/;s/õ/o/;s/ô/o/;s/Á/A/;s/Ò/O/;s/Ã/A/' -v {} \;
Das liegt an 2 Punkten:
1. ersetzt sowas

Code: Alles auswählen

rename 's/ä/ae/'
nur das erste Vorkommen von ä im Dateinamen. Hat der Dateiname 3 ä im Namen, dann musst du die Routine 3 mal laufen lassen. Abhilfe schafft die globale Ersetzung:

Code: Alles auswählen

rename 's/ä/ae/g'
Für einzelne Zeichen (im Such- und Erstzungsabschnitt) bietet sich die y///-Erstzung an, welche nach der Position im Abschnitt das entsprechende Pendant, direkt global ersetzt. Wird ein Zeichen durch 2 Zeichen ersetzt, oder andersrum, dann bleibt lediglich s///g.
2. liegt das an der Funktionsweise von find. Dir wird vermutlich aufgefallen sein, dass dort häufiger "Verzeichnis nicht gefunden" (oder Vergleichbares) aufgetreten ist. Find erstellt in einem Rutsch diese Liste und bearbeitet sie dann von oben nach unten ab. Meinetwegen wäre die Ergebnisliste sowas:

Code: Alles auswählen

./Ä
./Ä/ä.txt
Dann würde das erste rename das Verzeichnis "./Ä" ändern und das danach folgende rename würde aber die Datei "./Ä/ä.txt" nicht finden, da das Verzeichnis ja inzwischen umbenannt wurde und gar nicht mehr existiert. Der nächste Find-Lauf würde dann die Datei "./Ae/ä.txt" finden und rename würde ändern. Was du benötigst wäre eine Ausgabeliste in dieser Form:

Code: Alles auswählen

./Ä/ä.txt
./Ä
Dass die Dateien vor den Verzeichnissen ausgegeben werden, damit die Abarbeitung von oben nach unten durchlaufen kann. Das erreichst du durch den find-Parameter "-depth".
Wenn man das alles kombiniert und zusätzlich noch den Verbose-Schalter von rename an die richtige Stelle setzt (damit du auch was siehst) und dann noch den Turbo von find zündet, in dem man rename eine Argumentliste (+ anstatt \;) übergibt und damit nur insgesamt einen (sofern die Liste nicht zu lang ist), anstatt für jeden Dateinamen einen eigenen Rename-Prozess startet, dann sollte sowas in einem Rutsch performant durchlaufen:

Code: Alles auswählen

find ./ -depth -execdir rename -v 's/ä/ae/g;s/ö/oe/g;s/ü/ue/g;s/Ä/Ae/g;s/Ö/Oe/g;s/Ü/Ue/g;s/ß/ss/g;y/éóíúáãÊêàèâõôÁÒÃ/eoiuaaEeaeaooAOA/' {} +
2. Eine Ausführung reicht. Der Befehl "1." soll unbedingt vorher erfolgreich angewandt worden sein, sonst werden z.B. Zeichen mit Umlaut hier durch "_" ersetzt:

Code: Alles auswählen

rename 's/[^[:alnum:].]/_/g' *
Nur schade, dass ich diesen Befehl bisher nicht zum rekursiven Verhalten überreden konnte, Grund warum ich ihn in jefem Unterverzeichnis extra ausführen muss. Kennt jemand eine Lösung dafür?
Das funktioniert deswegen nicht, weil rename nicht rekursiv arbeitet, sondern nur die Argumente bearbeitet, die du übergibst (hier mit * die Dateien im aktuellen Verzeichnis). Man könnte die Argumente also ebenfalls mit find übergeben. Bei sowas wäre ich allerdings ziemlich vorsichtig, denn hier werden unterschiedliche Zeichen durch ein einziges Zeichen ersetzt, was womöglich Dubletten erzeugen will und damit Fehler hervorrufen dürfte.

3. Verbleibende "." und mehrfach nebeneinander stehende "_" werden hiermit auf einem einzigen "_" versetzt:

Code: Alles auswählen

find ./ -execdir rename 's/\.\_/\_/;s/\_\_/\_/' -v {} \;
Ein _ muss nicht gequotet werden und im Ersetzungsteil wird alles literal behandelt. Zudem kommt dieses rename ja von Perl, kann also mit + mehrere Zeichen ansprechen:

Code: Alles auswählen

find ./ -execdir rename -v 's/\._/_/;s/__+/_/' {} +
Noch offene Wüsche:

1. Jedem Befehl bei der Ausführung mitteilen, wie oft er ausgeführt werden soll. Vor allem beim 1. muss man mehrfach den Vorgang wiederholen. Da würde ich gerne ihm sagen können, mache jetzt 10 Durchläufe und dann höre auf.
Sollte obsolet sein!?
2. Dann die drei Befehle in einer einzigen Zeile packen (mein Wunsch "1." berücksichtigt), sodass der Befehl-Bündel nur einmal ausgeführt werden müsste. Das würde sehr hilfreich sein :)
Mir ist zwar nicht klar, warum das notwendig sein sollte, aber das gibt's mehrere Möglichkeiten: Du kannst die Befehle durch einen Strichpunkt trennen, dann werden die folgenden Befehle ausgeführt, wenn die davorstehenden Befehle abgearbeitet sind, unabhängig davon, ob erfolgreich oder nicht. Oder du kannst die Befehle mit && verketten, dann wird der folgende Befehl abgearbeitet, wenn der davorstehende Befehl erfolgreich (fehlerlos) abgearbeitet wurde. Oder in find direkt - find verkettet automatisch und wenn nicht anders angegeben mittels UND. Bedeutet, dass du einfach die sich unterscheidenden Teile (die -execdir) hintereinander aufführst:

Code: Alles auswählen

find ... +; find ... +
find ... + && find ... +
find ... -execdir ... + -execdir ... +
EDIT:
Die letzte Variante funktioniert natürlich nicht, wenn rename im 1. execdir angewendet wurde, da der dann aktuelle Dateiname ja nicht mehr in der Ergebnisliste auftaucht.

Che
Beiträge: 358
Registriert: 09.06.2006 19:00:33

Re: Skript bzw. Befehl zum Ersetzen von Zeichen in Dateinamen

Beitrag von Che » 22.05.2019 13:21:58

Viellen Dank für Deine ausführliche und sehr nützliche Antwort :)
Viele Grüße
che

Antworten