Home / LinuxUser / 2001 / 06 / The Answer Girl

Newsletter abonnieren

Lies uns auf...

Folge LinuxCommunity auf Twitter

Top-Beiträge

„Klickbunt auf dem Server? Warum nicht, wenn's hilft!"
(220 Punkte bei 8 Stimmen)
Wheezy + Kernel 3.9.2 + LXDE
(164 Punkte bei 4 Stimmen)
KDE 4.10.2, Gnome 3.6 und mehr
(143 Punkte bei 5 Stimmen)
Google schmeißt Jabber raus ...
(121 Punkte bei 4 Stimmen)

Heftarchiv

LinuxUser Heftarchiv

EasyLinux Heftarchiv

Ubuntu User Heftarchiv

Ubuntu User Heftarchiv

Partner-Links:

Das B2B Portal www.Linx.de informiert über Produkte und Dienstleistungen.

Bitte übersetzen Sie!

The Answer Girl

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
 […]
Einem Freund empfehlen    Druckansicht Bookmark and Share
Kommentare

Hits
Wertung: 130 Punkte (6 Stimmen)

Schlecht Gut

Infos zur Publikation

Infos zur Publikation

title_2013_06

Aktuelle Ausgabe kaufen:

Heft bestellen Heft als PDF kaufen

LinuxUser erscheint monatlich und kostet in der Nomedia-Ausgabe EUR 5,95 und mit DVD EUR 8,50. Weitere Informationen zum Heft finden Sie auf der LinuxUser-Homepage.

Im LinuxUser-Probeabo erhalten Sie drei Ausgaben für 3 Euro. Das Jahresabo (ab EUR 60,60) können Sie im Medialinx-Shop bestellen.

Tipp der Woche

Emacs als Psychologe
Emacs als Psychologe
Tim Schürmann, 21.05.2013 11:49, 0 Kommentare

Wer beim Schreiben in Emacs nicht mehr weiter weiß oder schier an einer Programmieraufgabe verzweifelt, der kann den eingebauten Psychologen um Rat fragen.

1966 entwickelte der Infor...

Aktuelle Fragen

Kontakt via QR code hinzufügen
Herman Sproesser, 17.05.2013 17:46, 3 Antworten
Hi hab in der aktuellen ausgabe 06.2013 den artikel über QR Codes gelesen. Ich hab nartürlich...
Dateimanager Dolphin, wo legt er die Datei Kommentare ab?
Uwe Heine, 16.05.2013 15:19, 1 Antworten
Suse Linux 12.1 - KDE 4.7.2 / Dolphin 1.7. Ich habe bei vielen Dateien über Dolphin Kommentare...
wie baut man Kernel Module so, dass sie mit jedem Kernel laden?
GoaSkin , 22.04.2013 10:22, 1 Antworten
Hallo, baut man anhand eines Kernel-Sources Module, nutzt Linux im Normalfall eine strikte V...
Problem beim Installieren von SuSe 12.3
Georg Prokert, 14.04.2013 00:04, 16 Antworten
Hallo, auf meinem neuem Laptop (Acer Aspire V3-771, Intel Core i5-3230 M, Intel HD Graphics 4...
lvm wieder loswerden
Peter Dibbern, 11.04.2013 16:28, 4 Antworten
Ich habe sämtliche alten Kisten ausgeschlachtet und aus der Teilen einen Rechner zusammengeschrau...