Skript bzw. Befehl zum Ersetzen von Zeichen in Dateinamen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Che
Beiträge: 358
Registriert: 09.06.2006 19:00:33

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

Beitrag von Che » 03.02.2015 13:54:31

Danke, Newdeb!

Noch zwei Fragen:

1. Gibt es eine Möglichkeit, das erste Zeichen einer Dateibezeichnung spezifisch anzusprechen, zum Beispiel wenn man am Anfang der Bezeichnung andere Zeichen hinzufügen möchte?

2. kann der Befehl rekursiv auf Unterverzeichnisse angewedet werden? Wenn ja, wie soll der Befehl genau aussehen?

Herzlichen Dank
che

newdeb
Beiträge: 134
Registriert: 03.02.2011 11:11:21
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Frankfurt

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

Beitrag von newdeb » 03.02.2015 18:47:49

Den Wort-/Namensanfang kannst du mit dem Zeichen '^' ansprechen, einen sogenannten Anker (eine gedachte, nicht druckbare Positionsreferenz in einem regulären Ausdruck).

Code: Alles auswählen

$ touch foo
$ rename -n 's/^/my/' foo
foo renamed as myfoo
Du kannst auch '^' mit nachfolgenden (Meta-)Zeichen kombinieren, um z.B. das erste Zeichen zu ersetzen ('.' -> beliebiges Zeichen im regulären Ausdruck)

Code: Alles auswählen

$ touch bar
$ rename -n 's/^./my.t/' bar
bar renamed as my.tar
Zuletzt geändert von Meillo am 03.02.2015 22:20:25, insgesamt 1-mal geändert.
Grund: s/°/^/

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

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

Beitrag von Che » 04.02.2015 03:57:06

Danke, Newdeb! Das ist genau was ich brauchte! Für die Frage 2 habe ich auf diese Seite interessante Beispiele gefunden, habe sie aber noch nicht ausprobiert:
http://forum.ubuntuusers.de/topic/verze ... nd-rename/
Viele Grüße
che

newdeb
Beiträge: 134
Registriert: 03.02.2011 11:11:21
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Frankfurt

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

Beitrag von newdeb » 04.02.2015 10:52:20

Che hat geschrieben:Für die Frage 2 habe ich auf diese Seite interessante Beispiele gefunden,
Ich würde die Antwort in diesem (deinem) Thread nehmen, die ist jüngeren Datums :wink:
Meillo hat geschrieben:

Code: Alles auswählen

find /path/to/dir | xargs rename 's/ä/ae/g'

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

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

Beitrag von Che » 04.02.2015 15:29:38

Danke für den Hinweis! Wende ich den Befehl an, bekomme ich die Meldung:

Code: Alles auswählen

find /path/to/dir | xargs rename 's/ä/ae/g'
xargs: unmatched einfache quote; by default quotes are special to xargs unless you use the -0 option
Mit diesem Befehl war es möglich, durch die zahlreichen Unterverzeichnisse durch zu kommen, ohne auf unerwarteten Zeichen hängen zu bleiben:

Code: Alles auswählen

 find ./ -execdir rename 's/ä/ae/;s/ü/ue/;s/ö/oe/'  -v {} \;
Was in diesem Unterverzeichnis liegt:

Code: Alles auswählen

patricia_shaw/Patrici_Shaw_Gelese_von_Philipp_Schepmann-1/Im_Tal der Mangobaume
01 - Titelnummer 1.ogg
02 - Titelnummer 2.ogg
wird mit diesem Befehl:

Code: Alles auswählen

find ./ -execdir rename 's/_-_ /_/;s/ä/ae/;s/ö/oe/;s/ü/ue/;s/Ä/ae/;s/, /_/;s/\._/_/;s/_-_/_/;s/ - /_/;s/ /_/' -v {} \;
jedoch nicht entsprechend geändert. Ausgegeben wird die Meldung "Datei oder Verzeichnis nicht gefunden". Leerzeichen im Verzeichnisname dürfen anscheinend nicht vorkommen.
Warum es problematisch ist, wenn im Pfad Großbuchstaben vorkommen (es wird im oben geposteten Link angesprochen) habe ich nicht verstanden. Mit dem relativen Pfad ./ gibt es derartige Probleme nicht. Auf der erwähnten Seite wird diesen zusammengestellten Befehl angegeben:

Code: Alles auswählen

 find ./ -execdir rename 's/ä/ae/;s/ü/ue/;s/ö/oe/'  -v {} \;
Wie kann ich das Ende einer Datei-Bezeichnung ansprechen? Gibt es eine Möglichkeit, nur Verzeichnisse bzw. nur Dateien (in den Unterverzeichnissen) anzusprechen? Zu einigen dieser Fragen habe ich hier unter Einsatz des Programms convmv was finden können: https://www.sebastian-siebert.de/2009/1 ... mbenennen/

Um Unterverzeichnisse umzubenennen (z.B. um die Leerzeichen durch anderes Zeichen zu ersetzen, um somit die dort enthaltenen Dateien umbenennen zu können), habe ich erfolgreich folgenden Befehl benutzt:

Code: Alles auswählen

find ./ -depth -type d -print0 | xargs -r -0 rename 's/\ /_/g'
Für jede Verzeichnisebene muss der Befehl anscheinend erneut ausgeführt werden, er wirkt Schrittweise immer eine Ebene tiefer. Erst danach kann der Befehl um die enthaltenen Dateinamen zu ändern ausgeführt (eventuell auch mehrfach) werden.

Folgende Erläuterungen fand ich hilfreich:
Erläuterung für find:
./ = In welchem Verzeichnis soll durchsucht werden (hier nur aktuelles)
-type f = Nur Dateien sollen berücksichtigt werden
-print0 = Jede Datei wird ein NULL-Zeichen als Trennzeichen angehängt, somit lassen sich die Dateiennamen bei der Übergabe an xargs besser auseinander halten

Erläuterung für xargs:
-r = nicht ausführen, wenn keine weiteren Argumente übergeben werden können (= leer)
-0 = NULL-Zeichen als Trennzeichen berücksichtigen.

Vielen Dank
che

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

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

Beitrag von Che » 01.09.2016 11:34:03

Hallo Debianer,
wie könnte ich den Ersetzungsbefehl so gestallten, dass alles, was kein Groß- oder Kleinbuchstabe ist und was keine Zahl ist durch "_" ersetzt wird?

Heute wollte ich eine BRD-ISO erstellen und K3B gab die meldung aus:

"/usr/bin/genisoimage: No such file or directory. Invalid node - '/pfad/Datei' "

Dabei habe ich festgestellt, dass ein komisches Viereckzeichen in der angegebenen Dateiname enthalten ist. Dieses Zeichen wird nur sichtbar, wenn man z.B. unter Nautilus das Umbebenne derselben aktiviert hat. Es handelt sich um eine aus dem Internet heruntergeladene Datei.

Das so viele unerwarteten für das Programm unersträglichen Zeichen vorkommen können, wäre es vermutlich einfacher, ein Befehl zu schreiben, der nur die Zeichen angibt, de erlaubt sind, und alles davon abweichende in "_" umwandelt. Geht das? Wie würde sowas aussehen?
Vielen Dank!
che

newdeb
Beiträge: 134
Registriert: 03.02.2011 11:11:21
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Frankfurt

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

Beitrag von newdeb » 01.09.2016 13:27:43

Code: Alles auswählen

rename 's/[^[:alnum:].]/_/g' datei ...
Der Punkt '.' ist hier ausgespart, um die Extension zu erhalten.

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

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

Beitrag von Che » 02.09.2016 14:51:33

Danke! Wenn ich jedoch diesen Befehl so ausführe:

Code: Alles auswählen

rename 's/[^[:alnum:].]/_/g' /media/che/Home-wheezy-sda3/che/Dokumente/*
Bekomme ich als Ausgabe eine Reihe von Meldungen wie diese:

Code: Alles auswählen

Can't rename /media/che/Home-wheezy-sda3/che/Dokumente/Unternehmen.odt _media_che_Home_wheezy_sda3_che_Dokumente_Unternehmen.odt: Ungültiger Link über Gerätegrenzen hinweg
Viele Grüße
che

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
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 15:26:03

Che hat geschrieben:

Code: Alles auswählen

rename

Code: Alles auswählen

Ungültiger Link über Gerätegrenzen hinweg
Tja, da haette man in der Implementierung statt rename(2) wohl besser mv(1) verwendet (bzw. dessen Vorgehen implementiert) ...
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 Dateiname

Beitrag von Che » 02.09.2016 23:11:30

Was hat das zu bedeuten? Wieso "über Gerätegrenzen hinweg", wenn im angegebenen Pfad eine normal heruntergeladene Webseite liegt? Sind vielleicht die darin befindlichen Verlinkungen gemeint?

Code: Alles auswählen

Tja, da haette man in der Implementierung statt rename(2) wohl besser mv(1) verwendet (bzw. dessen Vorgehen implementiert)
Entschuldigung, aber ich verstehe nicht wirklich, was du sagen willst.

Als ich weiter oben meinte, dass ein Befehl zur Umbenennung von Dateien und Verzeichnisse, der alle Buchstaben und alle Zahlen beibehalten würde und alles andere durch "_" ersetzt, habe ich auch Buchstaben mit Umlauten oder andere Diakritica als Buchstabe berechnet. Bei dem obigen Befehl scheinen Umlaute aber anders aufgafasst zu werden, denn "Einkäufe" wird zu "Eink__ufe". Wie kann das Berücksichtigt werden?

Viele Grüße
che

Benutzeravatar
Meillo
Moderator
Beiträge: 8782
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: 1964
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