Grep und paste

Als hätten wir uns nicht schon genug gemüht, stellt uns technik.vok vor eine ungleich schwierigere Aufgabe: Hier steht Original und Übersetzung jeweils in einer eigenen Zeile, von den restlichen Vokabeln ist das Paar durch jeweils eine Leerzeile abgetrennt:

 Ab-; Abfall
 waste
 abfuehren
 discharge[…]

Mit sed auf einer Kommandozeile wird das nichts mehr, denn hier müssen wir Zeilenumbrüche durch -- ersetzen und zusätzlich noch Leerzeilen eliminieren. Auch mit perl wird es schwierig, einen noch halbwegs verständlichen Einzeiler dafür zu bauen. Doch zum Glück ist die Datei so regelmäßig aufgebaut, dass – wenn wir einmal die Leerzeilen entfernt haben – immer eine ungerade und die darauf folgende gerade Zeile zusammengehören.

Die Leerzeilen bekommen wir weg, indem wir mit grep all jene Zeilen heraussuchen, in denen mindestens ein Buchstabe a-z und/oder A-Z vorkommt:

[trish@lillegroenn eng_deu]$  grep [a-zA-Z] technik.vok

Jetzt wird es etwas schwieriger. Doch da erinnern wir uns an das cut-Kommando, mit dem sich Spalten aus Textdateien extrahieren lassen. Wenn es cut gibt, muss es doch auch ein paste geben, dass mehrere Spalten zu einer Datei zusammenfügt. Tatsächlich werden wir mit man paste fündig.

Mit -d können wir einen Spaltentrenner angeben – leider nur einbuchstabig, aber gut, das können wir später noch mit sed ersetzen. Wichtig ist nur, dass der Delimiter nicht in technik.vok vorkommt. Wie wäre es mit #? Lassen Sie uns nachzählen (engl. count"):

[trish@lillegroenn eng_deu]$  grep -c "#" technik.vok
 0

Genau 0 Mal kommt das Hash-Zeichen ("#") in dieser Wörterbuchdatei vor und eignet sich daher hervorragend als temporärer Spaltentrenner für paste.

Der Rest ist ganz einfach: paste will als Argumente lediglich die beiden Dateien haben, die als erste und weitere Spalte(n) dienen. Nun haben wir zwar keine Dateien, aber die Man Page verrät, dass paste auch mit der Standardeingabe (z. B. aus der Pipe von grep) zufrieden ist, wenn wir statt eines Dateinamens ein - einsetzen.

Eigentlich können wir mit der Standardeingabe STDIN (standard input") ganz glücklich sein; diese hat nämlich die schöne Eigenschaft, dass eine Zeile aus STDIN verschwindet, sobald sie einmal ausgelesen wurde. Wenn wir paste in einem zugegebenermaßen üblen Hack zweimal STDIN unterschieben, bekommen wir genau den Effekt, den wir wollen: In der ersten Spalte stehen die ungeraden, in der zweiten Spalte die geraden Zeilen:

[trish@lillegroenn eng_deu]$  grep [a-zA-Z] technik.vok | paste -d "#" - -
 Ab-; Abfall#waste
 abfuehren#discharge[…]

Das Hash-Zeichen daraus zu entfernen, ist eine unserer leichtesten Übungen, und das Ergebnis leiten wir gleich in die Datei technik.vok_ um:

[trish@lillegroenn eng_deu]$  grep [a-zA-Z] technik.vok | paste -d "#" - - | sed -e "s/#/ – /" > technik.vok_

Das Ergebnis technik.vok_

Ab-; Abfall – waste
 abfuehren – discharge[…]

… kann sich und damit auch gleich in technik.vok umbenennen lassen.

Damit hätten wir eine genügende Auswahl Wörterbuchdateien (BOOK.VOK, EXERCISE.VOK, eng2ger.vok und technik.vok) am Platz – die Umwandlung der restlichen überlasse ich Ihrem Erfindungsreichtum – und können uns endlich einem kleinen Skript zuwenden, das die Übersetzung auf der Kommandozeile eingegebener Wörter übernimmt.

Einmal umdrehen

Von den hier benutzten vier Vokabeldateien weist BOOK.VOK einen gravierenden Unterschied zu den anderen auf: Der englische Begriff steht links, die deutsche Entsprechung rechts. Da das wb-Skript aus Listing 1 nicht erkennt, dass beispielsweise gestern -- yesterday aus eng2ger.vok und yesterday -- gestern aus BOOK.VOK für unsere Zwecke eine Dublette ist, ist es vermutlich am einfachsten, die Spalten in BOOK.VOK einfach umzudrehen.

Wie bei all den in diesem Answer-Girl vorgestellten Textmodifikationsübungen führen auch hier mehrere Wege zum Ziel; einige sollen an dieser Stelle exemplarisch aufgelistet werden.

Cut & Paste

Mit cut lassen sich Spalten aus einer Textdatei extrahieren, die mit paste wieder – auch in umgekehrter Reihenfolge – zusammengefügt werden können. Den Spaltentrenner geben wir explizit mit der Option -d (delimiter") an. Leider darf dieser nur ein Zeichen, keine Zeichenkette sein, und das macht das Ganze etwas umständlich:

[trish@lillegroenn eng_deu]$ sed -e "s/ – /%/" BOOK.VOK | cut -d "%" -f 1 > /tmp/BOOK.VOK.1
 [trish@lillegroenn eng_deu]$ sed -e "s/ – /%/" BOOK.VOK | cut -d "%" -f 2 > /tmp/BOOK.VOK.2
 [trish@lillegroenn eng_deu]$ paste -d "%" /tmp/BOOK.VOK.2 /tmp/BOOK.VOK.1 | sed -e "s/%/ – /" > /tmp/BOOK.VOK.paste

In den ersten beiden Zeilen ersetzen wir jeweils den echten Spaltentrenner -- durch das Arbeitstrennzeichen %. Zeile eins holt dann mit cut -f 1 alles heraus, was links neben dem Trennzeichen steht, und schreibt es in die temporäre Datei /tmp/BOOK.VOK.1. Dasselbe geschieht mit der zweiten Spalte (-f 2) rechts vom Trennzeichen in Zeile zwei – die Ausgabe dieser Ausschneideaktion mit cut landet in /tmp/BOOK.VOK.2. Wenn wir paste in der dritten Zeile als erstes Argument die zweite und als zweites Argument die erste temporäre Datei mitgeben, haben wir die Spalten aus BOOK.VOK vertauscht. Nun nur noch die Prozentzeichen wieder durch -- ersetzen und das Ergebnis der Umtauschaktion in /tmp/BOOK.VOK.paste speichern. Ist alles glatt gegangen, kann die Originaldatei damit überschrieben werden.

Perlen und Ausdrücke

Es geht natürlich auch weniger umständlich – doch dann gelangen wir in den Einflussbereich eigenständiger Skriptsprachen wie z. B. Perl. perl lässt sich mit der Option -p ganz gut als mächtigerer sed-Ersatz benutzen. Wie bei sed leitet die Option -e (execute") ein auf der Kommandozeile auszuführendes perl-Kommando ein.

[trish@lillegroenn eng_deu]$ perl -pe 's/(^.*)( – )(.*$)/$3$2$1/' BOOK.VOK > /tmp/BOOK.VOK.perl 

Ersetzt werden soll alles (.*) vom Anfang (^) einer Zeile bis zum Ende ($) durch eine umgeordnete Version. Damit der Zeileninhalt nicht verloren geht, speichern wir ihn in runden Klammern zwischen: den Anfang der Zeile vor dem Trennstring -- im ersten Puffer, -- im zweiten und den Rest bis zum Zeilenende im dritten Puffer. Ersetzt wird das Ganze jetzt durch den Inhalt des dritten Puffers ($3), gefolgt vom Trennstring aus dem zweiten ($2) und dem ehemaligen Zeilenanfang aus dem ersten Puffer ($1).

Beachten Sie, dass Sie das Perl-Substitute-Kommando in einfache Anführungszeichen (') setzen. Doppelte Anführungszeichen führen dazu, dass die Shell annimmt, mit $3$2$1 seien die Inhalte von Shell-, nicht Perl-Variablen gemeint.

Als wär's kein Problem

Der meiner Ansicht nach eleganteste Weg jedoch führt über awk. Im Gegensatz zu paste kommt dieses Tool nämlich auch mit Mehrzeichen-Spaltentrennern klar. Allerdings gibt man den Delimiter hier mit der Option -F (Field separator") an.

[trish@lillegroenn eng_deu]$ awk -F " – " '{print $2 " – " $1}' BOOK.VOK > BOOK.VOK.awk 

Das awk-"Programm" in einfachen Hochkommata besteht normalerweise aus einem Muster, auf das ein Kommandoblock in geschweiften Klammern angewandt wird. Da wir die gesamte Datei meinen, brauchen wir kein explizites Muster angeben und begnügen uns mit dem Klammerblock.

Darin weisen wir awk an, den Inhalt der zweiten Spalte ($2), dann den Trennstring -- und zum Schluss den Inhalt der ersten Spalte auszugeben.

Such mir mal

Wie (fast) jedes Shellskript beginnt es mit der Angabe, welche Shell wir verwenden. Natürlich die, mit der wir uns am Besten auskennen, und das wird meist die Linux-Standardshell bash sein:

#!/bin/bash -vx

Beim Entwickeln eines Skripts passieren oft Fehler, weshalb wir zunächst einmal die Debug-Optionen -vx einschalten.

Vorausgesetzt, in /usr/dict/eng_deu liegen nur konvertierte Wörterbuchdateien, halten wir dieses Wörterbuchverzeichnis in der Variable WBDIR fest:

WBDIR=/usr/dict/eng_deu

Wie bei jedem Skript, das für mehr als eine Person bestimmt ist, beginnen wir mit einer Aufrufprüfung: Wenn die Benutzerin mehr oder weniger als einen Suchbegriff als Argument eingibt (also ungleich (not equal") einen), …

if [ $# -ne 1 ]; then

… spucken wir einfach aus, wie unser Skript bedient werden möchte:

echo "Usage: $0 string"

Netterweise merkt sich ein Shellskript in der Variablen #, mit wievielen Argumenten es aufgerufen wurde. In der Variablen 0 (null) steckt das nullte Argument, also der Kommandoname selbst (ggf. mit angegebenem Pfad).

Im anderen Fall …

else

… suchen wir in den Vokabellisten im Verzeichnis $WBDIR nach dem ersten Kommandozeilenargument ($1):

        grep -hw "$1" $WBDIR/*

Mit der "Wort-Option" -w sorgen wir dafür, dass grep nur dann etwas ausgibt, wenn das Suchwort als solches (und nicht etwa als Bestandteil eines anderen Worts) in den Vokabellisten auftaucht.

Um Tippfehler bei der Groß- und Kleinschreibung auszuschließen, können wir grep auch noch dazu zwingen, Unterschiede in Groß- und Kleinbuchbuchstaben zu ignorieren:

        grep -hwi "$1" $WBDIR/*

… womit wir eigentlich schon fertig wären und die if-Konstruktion schließen können:

fi

Ausführbarkeitsrechte an unser wb-Skript vergeben …

[trish@lillegroenn /tmp]$ chmod ugo+x wb

… und testen:

[trish@lillegroenn /tmp]$ ./wb 
 #!/bin/bash -vx
 WBDIR=/home/trish/dict
 + WBDIR=/home/trish/dict
 if [ $# -ne 1 ]; then
         echo "Usage: $0 string"
 else
         grep -hwi "$1" $WBDIR/*
 fi
 + [ 0 -ne 1 ]
 + echo Usage: ./wb string"
 Usage: ./wb string

Dank der Geschwätzigkeitsoption -v ("verbose") zeigt die Bash jede einzelne Zeile an, die sie auszuführen gedenkt. Die Zeilen mit dem einleitenden Plus haben wir hingegen der Ausführlichkeitsoption -x ("extensive") zu verdanken, die jedes Mal auch angibt, was die Shell intern wirklich "sieht", wenn sie alle Ersetzungen vorgenommen (z. B. die Inhalte von Variablen ausgelesen) hat. Zum guten Schluss – und leider nicht besonders gekennzeichnet – finden wir in dem Wust natürlich auch noch die Ausgabe, die wir ohne Debug-Optionen zu Gesicht bekommen hätten, hier: Usage: ./wb string.

Auch die Variante mit einem Suchwort funktioniert:

[trish@lillegroenn /tmp]$ ./wb yesterday
 […]
 yesterday – gestern
 only yesterday – erst gestern
 yesterday – gestern
 gestern – yesterday
 vorgestern – the day before yesterday
 […]

LinuxCommunity kaufen

Einzelne Ausgabe
 
Abonnements
 

Ähnliche Artikel

  • The Answer Girl
    Dass der Computeralltag auch unter Linux des Öfteren für Überraschungen gut ist, ist eher eine Binsenweisheit: Immer wieder funktionieren Dinge nicht oder nicht so, wie eigentlich angenommen. Das Answer-Girl im LinuxUser zeigt, wie man mit solchen Problemchen elegant fertig wird.
  • The Answer Girl
    Dass der Computeralltag auch unter Linux des Öfteren für Überraschungen gut ist, ist eher eine Binsenweisheit: Immer wieder funktionieren Dinge nicht oder nicht so, wie eigentlich angenommen. Das Answer-Girl im LinuxUser zeigt, wie man mit solchen Problemchen elegant fertig wird.
  • The Answer Girl
    Wer hat sie nicht schon mehr als genug gehört, die gutgemeinte Litanei vom Anlegen eines Backups. In der Firma oder an der Uni mag man dieses leidige Thema mit einiger Berechtigung auf die dortigen Systemadministrator(inn)en abwälzen, doch was ist mit dem Datenbestand zu Hause?
  • The Answer Girl
    Dass der Computeralltag auch unter Linux des Öfteren für Überraschungen gut ist, ist eher eine Binsenweisheit: Immer wieder funktionieren Dinge nicht oder nicht so, wie eigentlich angenommen. Das Answer-Girl im LinuxUser zeigt, wie man mit solchen Problemchen elegant fertig wird.
  • Remote-Backups mit rsync und rdist
    Wenn klassische Datensicherungsmedien nicht in Frage kommen, jedoch genügend Plattenplatz, ein lokales Netzwerk oder eine gute (und billige) Internetanbindung zur Verfügung stehen, bietet sich ein alternatives Datensicherungsszenario an: das Spiegeln sicherungswürdiger Dateien.
Kommentare

Infos zur Publikation

LU 11/2014: VIDEOS BEARBEITEN

Digitale Ausgabe: Preis € 4,95
(inkl. 19% MwSt.)

Mit der Zeitschrift LinuxUser sind Sie als Power-User, Shell-Guru oder Administrator im kleinen Unternehmen monatlich auf dem aktuelle Stand in Sachen Linux und Open Source.

Sie sind sich nicht sicher, ob die Themen Ihnen liegen? Im Probeabo erhalten Sie drei Ausgaben zum reduzierten Preis. Einzelhefte, Abonnements sowie digitale Ausgaben erwerben Sie ganz einfach in unserem Online-Shop.

NEU: DIGITALE AUSGABEN FÜR TABLET & SMARTPHONE

HINWEIS ZU PAYPAL: Die Zahlung ist auch ohne eigenes Paypal-Konto ganz einfach per Kreditkarte oder Lastschrift möglich!       

Tipp der Woche

Schnell Multi-Boot-Medien mit MultiCD erstellen
Schnell Multi-Boot-Medien mit MultiCD erstellen
Tim Schürmann, 24.06.2014 12:40, 0 Kommentare

Wer mehrere nützliche Live-Systeme auf eine DVD brennen möchte, kommt mit den Startmedienerstellern der Distributionen nicht besonders weit: Diese ...

Aktuelle Fragen

Artikelsuche
Erwin Ruitenberg, 09.10.2014 07:51, 1 Antworten
Ich habe seit einige Jahre ein Dugisub LinuxUser. Dann weiß ich das irgendwann ein bestimmtes Art...
Windows 8 startet nur mit externer Festplatte
Anne La, 10.09.2014 17:25, 6 Antworten
Hallo Leute, also, ich bin auf folgendes Problem gestoßen: Ich habe Ubuntu 14.04 auf meiner...
Videoüberwachung mit Zoneminder
Heinz Becker, 10.08.2014 17:57, 0 Antworten
Hallo, ich habe den ZONEMINDER erfolgreich installiert. Das Bild erscheint jedoch nicht,...
internes Wlan und USB-Wlan-Srick
Gerhard Blobner, 04.08.2014 15:20, 2 Antworten
Hallo Linux-Forum: ich bin ein neuer Linux-User (ca. 25 Jahre Windows) und bin von WIN 8 auf Mint...
Server antwortet mit falschem Namen
oin notna, 21.07.2014 19:13, 1 Antworten
Hallo liebe Community, Ich habe mit Apache einen Server aufgesetzt. Soweit, so gut. Im Heimnet...