eoln2unix – Textdateien ins Unix-Format konvertieren

Headerbild: eoln2unix – Textdateien ins Unix-Format konvertieren

Bereits im Artikel „Dateikonvertierung unix2dos und dos2unix“ haben wir uns mit der Konvertierung von systemspezifischen Zeilenendungen befasst. Wenn man genau weiß, welche Zeilenendung eine Datei verwendet, sind die vorgestellten Skripten sehr nützlich. Häufig haben wir jedoch den Fall, Textdateien von unbekannten Systemen zu erhalten. Es wäre also praktisch, wenn wir ein Skript hätten, welches uns automatisch die erhaltene Textdatei in das Unix-Format wandeln kann, unabhängig davon, welche Zeilenendung in der Originaldatei verwendet wird. Ein solches Skript eoln2unix wollen wir in diesem Workshop entwickeln. Das Skript soll selbst erkennen, welches Zeilenendeformat vorliegt und dieses gegebenenfalls wandeln.

Zeilenendeformate

Sehen wir uns zuerst an, welche Zeilenendeformate wir überhaupt vorfinden können, die wir in unserem Skript erkennen müssen:

  • LF wird von Unix-Systemen verwendet, also auch von Gnu/Linux und macOS bzw. OS X, wie es bis vor Kurzem noch hieß.
  • CRLF wird von DOS bzw. Windows-Systemen verwendet.
  • CR wurde ehedem vom Mac-Rechnern benutzt, bevor sich Apple zu Unix bekannte.
  • LFCR könnte zumindest theoretisch vorkommen, auch wenn ich kein System kenne, welches diese Zeilenendung verwendet.

Jetzt stellt sich nur noch die Frage, wie wir die Zeilenenden erkennen. Dazu bietet sich das file-Kommando an.

Auf eine DOS-/Windows-Textdatei angewendet, meldet es beispielsweise:

$ file text.dos
text.dos: UTF-8 Unicode text, with CRLF line terminators

Auf die gleiche Datei im Unix-Format angesetzt, sieht das Ganze so aus:

$ file text.unix 
text.unix: UTF-8 Unicode text

Wer merkt etwas? – Nun, der interessante Teil des Ergebnisses ist der, der mit „with“ beginnt. Danach steht, in Leerzeichen eingeschlossen, die erkannte Zeilenendung. Bei Unix-Dateien schweigt das file-Kommando. Die Idee ist also, eine ausgewiesene Dateiendung zu extrahieren und dann fallweise, je nach Dateiendung, eine Konvertierung vorzunehmen.

Skript zur Zeilenendeerkennung

Schreiben wir uns ein kleines Test-Skript, um die erkannten Zeilenendungen auszugeben:

#!/bin/bash
# Zeilenendekennung extrahieren
eoln_type="$(file "$1" | sed 's/.*with //' | sed 's/ .*//')"
echo $eoln_type

Das Skript benutzt das sed-Kommando, um die Ausgabe von file zu filetieren. Wir entfernen alle Zeichen bis zu einem mögliche „with“, inklusive des folgenden Leerzeichens. Sodann wird alles weggeschnitten, was einer möglichen Zeilenendekennung folgt. Wenn eine Zeilenendekennung ausgegeben wurde, bleibt diese übrig und wird der Variablen eoln_type zugewiesen. Diese geben wir dann einfach aus.

Zeilenenden manuell konvertieren

Im Grunde sind die erforderlichen Operationen ganz einfach. Nehmen wir beispielsweise eine DOS-/Windows-Datei. Dann wandelt das folgende Kommando die Zeilenenden nach Unix um:

$ tr -d '\r' <text.dos >text.unix

Selbiges erreichen wir natürlich auch mit

$ cat text.dos | tr -d '\r' > text.unix

Wir löschen einfach das CR (Carriage Return) aus der Zeichenfolge CRLF , welches durch ‚\r‘ symbolisiert wird. Das LF (Line Feed) bleibt erhalten. Die Datei ist im Unix-Formt. Das gleiche Kommando funktioniert, wenn wir eine Datei bekommen, die die Zeilenenden mit LFCR markiert. – Welches System auch immer das tun mag.

Bei Mac-Dateien müssen wir ‚\r‘ durch ‚\n‘ ersetzen. Das Schema ist das gleiche.

Wir erkennen: Das Prinzip ist einfach.

eoln2unix – automatische Konvertierung von Zeilenenden

Die eigentliche Arbeit besteht darin, das Ganze in ein halbwegs stabiles Shell-Skript zu verpacken. Das folgende Skript zeigt eine mögliche Lösung. Es verwendet das getopt-Kommando, um mögliche Kommandozeilenoptionen zu verarbeiten. Das Kommando ist bitte nicht mit dem Builtin getopts der Bash zu verwechseln. Ich habe das Builtin absichtlich nicht verwendet, um das Skript möglichst einfach und kompatibel zu halten, auch wenn getopts über mehr Fähigkeiten verfügt.

#!/bin/bash
#
# eoln2unix
#
# Das Skript konvertiert unterschiedliche Zeilenendungen in das 
# Unix-Format. Es muss ein Dateiname angegeben werden, weil das 
# Skript die uebergebenen Dateien mit dem file-Kommando 
# ueberprueft.
#
# Im Falle eines Fehlers bricht das Skript ab!
#
# Exit-Codes:   0 - Erfolg
#               1 - Hilfe wurde aufgerufen
#               2 - Falscher Aufruf
#               3 - Datei nicht gefunden
#
# Autor: Karsten Brodmann <kb@punkt-akademie.de>
# Datum: 2017-02-24
#######################################################################

# Ausgabe der Gebrauchsanweisung
function usage 
{ 
  echo "eoln2unix [-h] file(s)"
  echo "           -h       display help and exit"
  echo "           file(s)  one or more files to convert"
  echo "The eoln2unix utility copies the input to stdout."
}

# Kommandozeilenargumente auswerten
args=$(getopt h $*)
if [ $? != 0 ]
then
  usage
  exit 2
fi
set -- $args
for i
do
  case "$i"
  in
    -h) usage
        exit 1
        ;;
    --) shift; break;;
  esac
done

# wenn kein Dateiname angegeben wurde, ist das ein Fehler
if [ $# -lt 1 ]
then
  usage
  exit 2
fi

# ok, alles als Dateinamen interpretieren
for f in "$@"
do
  if [ -f $f ]
  then
    # Zeilenendekennung extrahieren
    eoln_type="$(file "$f" | sed 's/.*with //' | sed 's/ .*//')"
    # je Nach Zeilenendekennung 'tr' aufrufen
    case "$eoln_type" in
      CR)                        # Mac: '\r' durch '\n' ersetzen
        cat "$f" | tr '\r' '\n'
        ;;
      CRLF|LFCR)                 # DOS|Windows,???: '\r' löschen
        cat "$f" | tr -d '\r'
        ;;
      *)  # Eingabedatei ist bereits eine Unix-Datei - nichts zu tun
        ;;
    esac
  else
    echo "$f does not exist" 1>&2
    exit 3
  fi
done
exit 0

Dank der Kommentare kann ich mir weitere Erläuterungen sparen. Der größte Aufwand besteht darin, das Skript angenehm in der Nutzung zu gestalten. Man kann damit mehrere Dateien, unabhängig von ihrem Typ, ins Unix-Format wandeln. Weil das Skript nur auf der Standardausgabe ausgibt, kann das Resultat via Ausgabeumlenkung nur in einer gemeinsamen Datei gesammelt werden.

$ eoln2unix datei1 >ergebnis1
$ eoln2unix datei1 datei2 >ergebnis12

Verbesserungen

Nur auf die Standardausgabe ausgeben zu können, ist ersteinmal eine Einschränkung. Es steht jedoch jedem frei, eine weitere Option zu realisieren, die es ermöglicht, jede Datei direkt zu konvertieren, also die ursprüngliche Eingabedatei zu überschreiben. Alternativ kann man sich natürlich auch des Skriptes overwrite, welches im UNIX-Werkzeugkasten vorgestellt wird, bedienen.

Nehmen wir an, wir hätte mehrere DOS-/Windows-Dateien namens dos1 bis dos6, so könnten wir diese in einer Rutsche mittels

$ for i in dos?
> do 
> overwrite $i eoln2unix $i
> done

konvertieren. Das Skript muss also nicht zwingend erweitert werden. Es ist auch in dieser einfachen Form schon sehr nützlich und bequem zu verwenden!

Karsten Brodmann

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.