[gelöst] Dateien umbenennen: utf8 nach iso88591

Mornsgrans

Help-Desk
Teammitglied
Registriert
20 Apr. 2007
Beiträge
71.942
Ich habe das leidige Problem, dass unter Linux Dateinamen, die Umlaute in UTF-8 enthalten, beispielsweise als "ändern.doc" dargestellt werden. Das Linux selbst darf ich nicht auf UTF-8 umstellen, da es dann an anderen Ecken krachen würde.

Auf der Suche nach einer Lösung bin ich über convmv gestolpert, was ein wirklich nettes, aber für mich leider auch unbrauchbares Tool ist, da es abbricht, sobald es über einen Dateinamen stolpert, das bereits Umlaute im Zielzeichensatz enthält, anstatt einfach diese Datei zu überspringen.

Ich suche jetzt ein Skript, das rekursiv diese Arbeit übernehmen kann, der Art
Code:
 mv "$datei" "`echo $datei|recode utf8..iso88591`"
Da Dateinamen auch Leerstellen enthalten, kann ich nicht einfach mit "find" und darauf folgender "for" - Schleife arbeiten.

Hat jemand eine Idee, wie man das Problem lösen kann?
 
Zuletzt bearbeitet:
Bei solchen Problemen habe ich immer Perl angeschmissen. Mit Bash kriegen die Shell Koriphäen so etwas elegant hin und man liest mit Ehrfurcht die genialen Zeilen aber für einen Otto Unbegabt fand ich die Kontrollstrukturen und Variablen bei Perl wesentlich verständlicher.
 
Der Trick bei find besteht im Wesentlichen darin, für die Verarbeitung weitere Shells heranzuziehen:
Code:
find ordner...  -type f -exec sh -c 'set -e; from="{}"; to="$(echo "{}" | recode utf8..iso88591)"; [ "$from" = "$to" ] || echo -- "$from" "$to"' \;

## oder (Einzelabarbeitung mit xargs)
find ordner...  -type f -print0 | xargs -0 -n 1 -I '{FILE}' sh -c 'set -e; from="{FILE}"; to="$(echo "{FILE}" | recode utf8..iso88591)"; [ "$from" = "$to" ] || echo -- "$from" "$to"'

## oder (Verarbeitung mehrerer Dateien in einer Shell)
find ordner...  -type f -print0 | xargs -0 sh -c 'set -e; while [ $# -gt 0 ]; do from="${1}"; to="$(echo "${1}" | recode utf8..iso88591)"; [ "$from" = "$to" ] || echo -- "$from" "$to"; shift; done;'

Das letzte echo jeweils durch mv ersetzen und los geht's ;)


find+for ginge im Übrigen in etwa so (vorausgesetzt, die Dateien haben kein <newline> im Namen):
Code:
OLDIFS="$IFS"
IFS='
'
set -f
set -- $(find ordner... -type f)
set +f
IFS="$OLDIFS"

for file; do
   ...
done
 
Zuletzt bearbeitet:
wie wäre es mit rsync (siehe unten). Vorsicht mit der Option --delete-after. Bitte teste es zuerst in einem Testfolder!

Code:
$ ls -1
äüöß.txt
mit leerzeichen ßöüä.txt
$ rsync -av --iconv=utf8,iso88591 . . --delete-after # von utf8 in iso88591
building file list ... done                                                                     
mit leerzeichen ßöüä.txt
äüöß.txt
deleting äüöÃ.txt
deleting mit leerzeichen Ãöüä.txt

sent 184 bytes  received 54 bytes  476.00 bytes/sec
total size is 0  speedup is 0.00
$  ls -1
mit leerzeichen ????.txt
????.txt
$ rsync -av --iconv=iso88591,utf8 . . --delete-after  # und zurück von iso88591 in utf8
building file list ... done                                                                     
mit leerzeichen \#337\#366\#374\#344.txt
\#344\#374\#366\#337.txt
deleting äüöß.txt
deleting mit leerzeichen ßöüä.txt

sent 176 bytes  received 50 bytes  452.00 bytes/sec
total size is 0  speedup is 0.00
$ ls -1
äüöß.txt
mit leerzeichen ßöüä.txt
 
Das werde ich morgen mal testen. Erst einmal vielen Dank.
 
Da Dateinamen auch Leerstellen enthalten, kann ich nicht einfach mit "find" und darauf folgender "for" - Schleife arbeiten.

Schau dir mal die Optionen -print0 von find und -0 von xargs an, damit lässt sich das so lösen:
find . -print0 | xargs -n 1 -0 convmv

Das ganze klingt mir aber eher danach, dass dein Terminalemulator Probleme mit UTF8 hat. In dem Fall einfach einen anderen verwenden bzw. die Konfiguration anpassen. Was systemweit eingestellt ist, ist ja hauptsächlich eine Vorgabe, die der User entsprechend anpassen kann.

Uli
 
Zuletzt bearbeitet:
Vor lauter Leerzeichenproblematik hab ich vergessen, dass es ja auch noch -exec gibt: Es geht also noch einfacher: find . -exec convmv "{}" \;

Uli
 
Der "Terminalemulator" ist ein einfaches PuTTy und bereitet keine Probleme.

convmv "works as designed", wenn es abbricht. Nur hilft es mir nicht weiter, wenn ich Dateien habe, deren Namen mal im ISO-88591-1 - Format und mal im UTF-8 - Format vorliegen.

Die Vorschläge von uli42 funktionieren, wenn auch recht langsam. Die aus den Beiträgen darüber muss ich noch versuchen.
 
Zuletzt bearbeitet:
Der obere Lösungsvorschlag von dyvi funktioniert leider auch nur, solange die Ordnernamen entweder keine Umlaute oder aber Umlaute im UTF-8 - Zeichensatz haben. Wurde der Ordnername nach ISO8859-1 konvertiert, erhalte ich nur noch
Code:
recode: Invalid input in step `UTF-8..ISO-8859-1'

Der untere Lösungsvorschlag "find+for" bedarf einiger Erläuterungen:
  1. - welche Ausprägung hat die Variable $IFS?
  2. - IFS='
    '
    wird da einfach nur nach dem ersten Hochkomma ein Zeilenumbruch durchgeführt?
  3. set -- $(find ordner... -type f)
    - es müssen Ordner- und Dateinamen konvertiert werden
 
Der "Terminalemulator" ist ein einfaches PuTTy und bereitet keine Probleme.

convmv "works as designed", wenn es abbricht. Nur hilft es mir nicht weiter, wenn ich Dateien habe, deren Namen mal im ISO-88591-1 - Format und mal im UTF-8 - Format vorliegen.

Die Vorschläge von uli42 funktionieren, wenn auch recht langsam. Die aus den Beiträgen darüber muss ich noch versuchen.

Naja, es wird halt convmv für jedes Objekt aufgerufen. Wenn convmv sich wie beschrieben verhält, sehe ich keine Möglichkeit, das schneller hinzubekommen (abgesehen von parallelen Abarbeiten mehrerer Verzeichnisse). Einen Patch, der verhindert, dass convmv abbricht, gibt es unter http://superuser.com/questions/602406/how-to-force-convert-with-convmv. Dort ist auch erklärt, warum das keine ganz so gute Idee ist.

Generell sehe ich aber dein Vorhaben als den falschen Ansatz an, denn eine Datenhaltung in UTF8 ist die einzig sinnvolle, alles andere sind mehr oder weniger funktionierende Kompromisse. Wenn schon, dann solltest du alle Dateien NACH UTF8 konvertieren, um in Zukunft diese /&$%/&$%-Probleme irgendwann mal komplett loszuwerden. In die andere Richtung zu gehen bedeutet im Grunde nur, dass du dich irgendwann wieder mit dem Problem befassen musst.

Du schreibst oben "Putty bereitet keine Probleme". Ich denke aber, dass genau Putty das Problem ist, denn der Terminalemulator ist entscheidend für die Darstellung verantwortlich. Ich hab mal ein wenig mit Putty herumgespielt: UTF-8-Dateien werden in Putty unsauber dargestellt, wenn dort unter Window/Translation/Remote character set was anderes als UTF-8 eingestellt ist (das ist sogar mit Putty unter Wine nachvollziehbar). Was steht da bei dir? Und worauf sind deine TERM und LANG-Variablen gesetzt?

Uli

Edit: typo
 
Zuletzt bearbeitet:
Der obere Lösungsvorschlag von dywi funktioniert leider auch nur, solange die Ordnernamen entweder keine Umlaute oder aber Umlaute im UTF-8 - Zeichensatz haben. Wurde der Ordnername nach ISO8859-1 konvertiert, erhalte ich nur noch
Code:
recode: Invalid input in step `UTF-8..ISO-8859-1'

Das recode hatte ich ungeprüft von dir übernommen, zielführender wäre in dem Fall wohl aber zu prüfen, ob die Konvertierung erfolgreich verlief, s.u.


Der untere Lösungsvorschlag "find+for" bedarf einiger Erläuterungen:
  1. - welche Ausprägung hat die Variable $IFS?
  2. - IFS='
    '
    wird da einfach nur nach dem ersten Hochkomma ein Zeilenumbruch durchgeführt?
  3. set -- $(find ordner... -type f)
    - es müssen Ordner- und Dateinamen konvertiert werden

Zu 2: ja, ist einfach nur ein Zeilenumbruch ("\n").

Zu 3: Das "-type f" einfach weglassen, wenn Order konvertiert werden sollen - ggf. "-mindepth 1" hinzufügen.

IFS (Internal Field Separator) ist eine Menge von Trennzeichen, die zum Aufteilen einer Zeichensequenz dient.
Hier ist die Zeichensequenz u.a. die Ausgabe von find, und es gibt nur ein Trennzeichen, <newline>

Ein Standardbeispiel ist das zeilenweise Einlesen von csv-Dateien und die anschließende Verarbeitung der einzelnen Felder:

Code:
# vorherigen IFS merken
DEFAULT_IFS="$IFS" 

# globbing deaktivieren,
#  sonst könnten Felder mit Inhalt "a*" usf. beim Ausführen von "set -- $line" falsch expandiert werden
set -f

# Zeilen lesen
while read -r line; do
   # Zeile in Felder unterteilen
   ## Trennzeichen ist das Komma
   IFS=","

   ## Expandieren/Word-Splitting der Datenfelder, Abspeichern in $@
   set -- $line
   ## Feld 1 ist jetzt in $1 gespeichert, Feld 2 in $2, ...

   ## IFS zurücksetzen
   IFS="$DEFAULT_IFS"

   # irgendwas damit machen
   process_data "$@" || break
done < ./data.csv


Die find+for-Variante Im vorliegenden Fall mit oben aufgeführten Veränderungen dann in etwa:

Code:
#!/bin/sh
set -e

CSET_FROM="UTF-8";
CSET_TO="ISO-8859-1"
NEWLINE="
"

OLDIFS="$IFS"
IFS="$NEWLINE"
set -f
set -- $(find ...)
set +f
IFS="$OLDIFS"


for src_file; do
   if \
      dst_file="$(echo "$src_file" | recode "${CSET_FROM}..${CSET_TO}" 2>/dev/null)" && \
      [ "$src_file" != "$dst_file" ]
   then
      echo mv -- "$src_file" "$dst_file"
   fi
done

Das ist POSIX-konformes sh, die bash-Variante wäre etwas kürzer/hübscher.

Idealerweise würdest du die Kodierung von "$src_file" vor "echo ... | iconv ..." prüfen und da bist du letztlich mit convmv, python+chardet, perl+Encode::Detect, ... besser aufgehoben, für sh fällt mir da momentan nur 'ne ziemliche Holzhammermethode mittels tmpfile/"file -i" ein.
 
So, ich habe das Problem durch das Knie über die Brust ins Auge gelöst.

Wie gesagt, zur zeit MUSS ich noch mit ISO-8859 arbeiten u.a., weil noch einige XP-Embedded und sogar W2K-Clients dranhängen (die mit UTF-8 ihre liebe Not haben) und einen Server setzt man auch nicht von heute auf morgen im Hau-ruck-Verfahren auf.

Danke dyvi für Deine ausführliche Erklärung, ich muss sie mir noch mal in Ruhe reinziehen.

Ich habe die Angelegenheit jetzt so gelöst, dass ich bereits beim Backup auf den Clients im tar-Befehl den Parameter --format=posix ergänzt habe (über diese Option bin ich heute beim Googeln nach etwas anderem "gestolpert") und so die Datei- und Verzeichnisnamen direkt bei der Sicherung in den passenden Zeichensatz konvertiere.

Die Dateien und Verzeichnisse, die im UTF-8 - Format vorliegen, muss ich somit nur einmal nach ISO-8859-1 konvertieren, wobei die oben vorgeschlagenen Lösungsansätze eine gute Hilfe waren, da dieser Durchgang so nur genau einmal durchgeführt werden muss und damit die Fehlermeldungen von recode bzw. convmv ausbleiben, da es sich hier um komplette Unterverzeichnisse handelt, die nur in UTF-8 vorliegen.

Die Crux war, dass auf der Server-Seite bash-Befehle zur Verfügung stehen, auf Clientseite durch eine Datenbankanwendung jedoch Cygwin-Bash und MS-DOS-Befehle miteinander verzahnt ausgeführt werden und gerade diese DOS-Shell unter Windows 7 mit UTF-8 gar nicht und mit ISO-8859-1 nur bedingt klarkommt.
 
Die Crux war, dass auf der Server-Seite bash-Befehle zur Verfügung stehen, auf Clientseite durch eine Datenbankanwendung jedoch Cygwin-Bash und MS-DOS-Befehle miteinander verzahnt ausgeführt werden und gerade diese DOS-Shell unter Windows 7 mit UTF-8 gar nicht und mit ISO-8859-1 nur bedingt klarkommt.

Klingt fast so, als ob da noch jemand außer mir Bedarf für eine Windows-Kommandozeile hat, die mehr als DOS-Codepages versteht. ;)

Guido
 
Schon mal was von Powershell gehört?

Nicht schon wieder ... Powershell ist nur eine Scriptsprache. Ruft man die powershell.exe auf, kriegt man wieder eine Konsole mit DOS-Zeichensatz. In der Entwicklungsumgebung PowershellISE mag das anders sein, aber das ist keine interaktive Kommandozeile.

Guido
 
Ich brauche keine interaktive Umgebung, es müssen lediglich aus einer Anwendung heraus Cygwin-Shellskripte und DOS-Batch-Dateien im Hintergrund ausgeführt werden. Da Cygwin den ATTRIB-Befehl nicht kennt, muss ich notgedrungen auf die elenden DOS-Anweisungen zurückgreifen.



Um das Ganze noch zu verkomplizieren:
Das Vordergrundprogramm arbeitet mit dem Zeichensatz HPROMAN-8, Cygwin und Windows 7 mit UTF-8 und die DOS-Box mit seinem eigenen.

Somit müssen für eine Operation die HPROMAN-8 Daten für das Bash-Skript nach UTF-8 konvertiert werden und ruft es seinerseits nun über die DOS-Shell den Attrib-Befehl auf, müssen die Daten nach ASCII konvertiert werden.

Ist eine recht komplizierte Angelegenheit, aber ich kann die Vordergrundanwendung nicht einfach auf UTF-8 umstellen, was einen Teil der Probleme beseitigen würde, da es sich um unternehmenskritische Daten (Fakturierung, Buchhaltung, Archivierung) handelt.
 
Zuletzt bearbeitet:
Danke für den Link. Das werde ich mir am Montag näher ansehen müssen.
 

Volltreffer! Das war er passende Tip. Das Wichtigste ist: man muß auch den verwendeten Font auf einen der anderen angebotenen umstellen! Dann geht sowohl UTF8 als auch UTF7 oder auch ISO 8859-1. Wechselt man nur die Codepage, gibts trotdzem keine Sonderzeichen. Mir stellt sich nur eine Frage: was haben sich die Entwickler dabei nur gedacht? :facepalm: Auch einige Effekte beim Einstellen des Fonts sind sehr seltsam ...

Guido
 
  • ok1.de
  • ok2.de
  • thinkstore24.de
  • Preiswerte-IT - Gebrauchte Lenovo Notebooks kaufen

Werbung

Zurück
Oben