Farbig angezeigte Treffer bei Suchabfragen in Textdateien. Und ein neues Verfahren, die Bedienungsanleitung zu einem Skript bei Bedarf anzuzeigen.
Wie schön wär's, wenn der Unix-grep
endlich Perls reguläre Ausdrücke verstünde!
Was gäb' ich, wenn in den passenden Zeilen auch noch die Treffer
farbig markiert wären! Ja hätt' ich dieses Tool nur in
meinem Werkzeugkasten! Heute macht der Perl-Onkel Träume wahr.
Das heute vorgestellte Skript cgrep bietet in der Tat die gewünschte
Funktionalität. Es erwartet auf der Kommandozeile einen regulären
Ausdruck und durchstöbert die angegebenen Dateien oder die Standardeingabe
nach dem Suchmuster. Gefundene Zeilen gibt es aus und unterlegt die
Treffer farbig.
Abbildung 1 zeigt, wie cgrep sein eigenes Listing nach dem Wort
rex durchsucht. Das Kommando cgrep rex cgrep bringt sieben Zeilen
zum Vorschein, die insgesamt neunmal das Wort rex lila unterlegen.
|
| Abb.1: Suche nach dem Wort rex |
Da cgrep nicht nur greps reguläre Ausdrücke versteht, sondern
den vollen Perl-5-Satz, kann man, wie in Abbildung 2 demonstriert, auch
nach alleinstehenden Ziffern suchen. Der reguläre Ausdruck hierzu lautet
\b\d+\b: Eine Wortgrenze, gefolgt von einer oder mehreren Ziffern,
gefolgt von einer abschließenden
Wortgrenze. Dieses Muster findet zwar die Fünf in Perl 5, aber
zum Beispiel nicht Perl5, da im zweiten Fall keine Wortgrenze zwischen
Perl und 5 liegt.
|
| Abb.2: Suche nach alleinstehenden Zahlen |
Das Modul Term::ANSIColor bietet die Funktion colored an, die
einen ihr übergebenen Text mit Kontrollsequenzen versieht, die ihn
farbig unterlegen, falls er auf einem Terminal ausgegeben wird.
colored erwartet zwei Parameter: Einen Skalar, der den einzufärbenden
Text enthält und einen weiteren Skalar, der die Malerarbeiten als Vorder-
und Hintergrundfarbe beschreibt. 'white on_blue' selektiert
beispielsweise weißen Text auf blauem Hintergrund:
use Term::ANSIColor;
print "Der ",
colored("Himmel", "white on_blue"),
" über Bayern.\n";
Dabei exportiert das Modul Term::ANSIColor die Funktion colored
automatisch in das Hauptprogramm, so dass dieses colored ohne
Modulzusatz aufrufen kann. Mehr zu diesem Thema und weiteren
nützlichen Funktionen von Term::ANSIColor findet sich in [1].
Listing cgrep zeigt, wie's geht: Es zieht zunächst die Zusatzmodule
Term::ANSIColor zum Einfärben, Getopt::Std zur Kommandozeilenanalyse
und das weiter unten detailliert besprochene Pod::Usage zum
eleganten Ausdrucken der Bedienungsanleitung herein. Wer noch nicht
perl 5.6.0 fährt, muss die Module von Hand nachinstallieren,
Abschnitt Installation zeigt wie.
Die Funktion getopts in Zeile 12 holt eventuell gesetzte Schalter
-h, -p und -i von der Kommandozeile und setzt dementsprechend
die Hasheinträge $opt{h}, $opt{p} oder $opt{i}.
Zeile 16 holt den regulären Ausdruck als String von der Kommandozeile,
Zeile 19 druckt eine Fehlermeldung mit Hilfestellung aus und bricht das
Programm ab, falls vergessen wurde, einen Ausdruck anzugeben.
Falls der Benutzer cgrep den Schalter -i zum Ignorieren von
Groß- und Kleinschreibung mitgab, stellt Zeile 22 den regulären
Ausdruck den String (?i) voraus, was Perl --
wie der Modifizierer /.../i --
dazu veranlasst, beim Pattern-Matching keinen Wert auf
Groß- oder Kleinschreibung zu legen.
Zeile 25 compiliert den Ausdruck, der ja als String vorliegt,
mit dem qr-Operator in einen Regex-Ausdruck.
Dies verursacht einen
tödlichen Fehler, falls der Ausdruck syntaktisch nicht den Perl-5-Bedingungen
entspricht.
Das eval-Konstrukt fängt dies ab, gibt im Fehlerfall einen falschen
Wert zurück und mit pod2usage eine Fehlermeldung
einschließlich Hilfestellung aus.
Geht alles soweit gut, iteriert Zeile 29 über alle Zeilen der auf der Kommandozeile namentlich bereitgestellten Dateien, oder, falls sich keine Namen finden, über alle Zeilen aus dem Standard-Input.
Zeile 32 prüft, ob die aktuell analysierte Zeile dem regulären Ausdruck
genügt. Falls sich ein Treffer findet,
ruft das Suche-Ersetze-Konstrukt in den Zeilen 35-37 dafür
die Färbefunktion colored aus dem Term::ANSIColor-Modul
auf. Der Modifizierer /g stellt sicher, dass dies für alle
Treffer in einer Zeile geschieht, und auch falls der reguläre Ausdruck
aus einer Reihe von Alternativen (wie in /a|b|c/) besteht, sorgt
die Treffervariable $& dafür, dass jeweils der richtige
Text eingefärbt wird.
Der Modifizierer /x erlaubt es, den Regex-Salat der Übersicht halber
auf mehrere Zeilen zu verteilen, und /e sorgt dafür, dass perl
den Ersetzungsausdruck als Code interpretiert, also tatsächlich die
Funktion colored() aufruft und nicht eine sture Text-für-Text-Ersetzung
durchführt.
Traf der Ausdruck die aktuelle Datenzeile nicht, druckt sie
cgrep in Zeile 42 dennoch aus, falls der Benutzer den Schalter
-p auf der Kommandozeile setzte.
Noch ein Beispiel: Zwei gleiche Worte, die direkt hintereinanderstehen, deuten meistens auf einen Tippfehler hin -- schau'n wir mal:
cgrep '\b(\w+)\s+\1\b' cgrep
sucht nach einer Wortgrenze, einem Wort, Leerzeichen und das gleiche
Wort nochmal in Folge, abgerundet von einer Wortgrenze. Und, in der Tat
in Zeile 28 von Listing cgrep hat sich ein Fehler eingeschlichen,
wie Abbildung 3 zeigt.
|
| Abb.3: Huch! Ein Tippfehler in cgrep! |
Das war's schon. Und damit auch jeder weiss, wie cgrep funktioniert --
auch die Leute, die zu faul zum Manualseitenlesen sind --, nutzt cgrep
das Modul Pod::Usage, das mit der Funktion pod2usage die Möglichkeit
bietet, die in ein Skript mit
POD (Plain Old Documentation)
eingebettete Dokumentation teilweise
oder ganz im Textformat auszugeben.
Jedes Perl-Modul enthält ja mittlerweile mit POD-Tags
ausgezeichnete Dokumentation.
Der perl-Interpreter ignoriert diese Information geflissentlich und
Werkzeuge wie perldoc oder die Filter pod2man, pod2html etc.
extrahieren sie auf Wunsch und wandeln sie in das gewünschte Zielformat um.
Der Aufruf perldoc Skriptname generiert üblicherweise
eine Manualseite aus dem Skript und zeigt sie an.
Das Modul
Pod::Usage hingegen erlaubt es, auf die eingebettete Dokumentation
schon aus dem Skript heraus zuzugreifen -- üblicherweise falls
seitens des Benutzers ein Eingabefehler vorliegt und das Skript
die fällige Fehlermeldung mit einer mehr oder weniger detailierten
Bedienungsanleitung versehen will.
pod2usage gibt üblicherweise eine ihr als String übergebene
Fehlermeldung aus, und druckt außerdem die Sektionen SYNOPSIS,
OPTIONS und ARGUMENTS einer eingebetteten Manualseite. Danach
bricht pod2usage es das Skript ab.
Der optionale Parameter -verbose steuert die Leutseligkeit
des Programms -- für den Wert 0 gibt es nur die SYNOPSIS-Abteilung
aus, für den Wert 1 die oben erwähnten drei Sektionen und für Werte
größer gleich 2 die gesamte
Manualseite. Die POD-Anweisungen nutzt perl dazu, einen ansprechend
formatierten Text zu generieren. Typische Anwendungen von pod2usage
sind:
# SYNOPSIS und OPTIONS/ARGUMENTS
pod2usage( $message );
# Nur SYNOPSIS
pod2usage( -message => $message,
-verbose => 0 );
# SYNOPSIS und OPTIONS/ARGUMENTS
pod2usage( -message => $message,
-verbose => 1 );
# Gesamte Manualseite
pod2usage( -message => $message,
-verbose => 2 );
Ruft so jemand das Skript cgrep ohne regulären Ausdruck auf, springt
Zeile 19 ein und ruft pod2usage auf, welches die Ausgabe nach
Abbildung 4 liefert, die dem Benutzer die richtige Anwendung des
Programms nahelegt.
|
| Abb.4: Hilfe nach Aufruf ohne Parametern |
Verlangt der Benutzer hingegen explizit nach der Manualseite, indem
er cgrep -h aufruft, verzweigt Zeile 14 zu pod2usage und stellt
mit verbose-Level 2 den Dokumentationsmodus ein. Abbildung 5
zeigt die Textdarstellung im X-Terminal.
|
| Abb.5: Vollständige Manualseite nach cgrep -h | less |
Natürlich steht auch nichts im Wege, den POD-Text in cgrep im
Unix-üblichen Manualseitenformat anzuzeigen:
pod2man cgrep | nroff -man | less
extrahiert die Information aus dem Skript und übergibt sie dem
Unix-üblich Manualformatierer nroff, der diese wiederum
durch den Pager less pfeift.
Und auch vor HTML scheut sich POD nicht:
pod2html cgrep >cgrep.html
erzeugt HTML-Text aus dem POD-Markup und legt ihn in cgrep.html ab.
Abbildung 6 zeigt, wie der Browser die Manualseite cgrep.html
darstellt.
|
| Abb.6: Die Manualseite als HTML im Browser |
001 #!/usr/bin/perl -w
002 ##################################################
003 # cgrep - Highlight regular expression matches
004 #
005 # Mike Schilli, 2000
006 ##################################################
007
008 use Term::ANSIColor;
009 use Pod::Usage;
010 use Getopt::Std;
011
012 getopts( 'hpi', \%opt );
013
014 pod2usage(-verbose => 2) if $opt{h};
015
016 my $rex = shift;
017
018 ### Called without regex?
019 pod2usage("Missing argument") unless defined $rex;
020
021 ### Regex modifiers if requested
022 $rex = "(?i)$rex" if $opt{i};
023
024 ### Compile regex and complain if it's broken
025 eval { $rex = qr($rex) } or
026 pod2usage "Invalid Regex: $rex";
027
028 ### Loop over all all lines of input
029 while(<>) {
030
031 ### Match?
032 if( /$rex/ ) {
033
034 ### Colorize with Term::ANSIColor
035 s/$rex
036 /colored($&, 'yellow on_magenta')
037 /egx;
038
039 print;
040 } else {
041 ### Print anyway if -p
042 print if $opt{p};
043 }
044 }
045
046 __END__
047
048 =head1 NAME
049
050 cgrep - Highlight regular expression matches
051
052 =head1 SYNOPSIS
053
054 cgrep [options] regex [file ...]
055
056 Options:
057 -h get help
058 -p print all lines
059 -i ignore case
060
061 =head1 OPTIONS
062
063 =over 8
064
065 =item B<-h>
066
067 Prints this manual page in text format.
068
069 =item B<-p>
070
071 Prints all lines, no matter if they match or not.
072
073 =item B<-i>
074
075 Activate "ignore case" mode in the regular expression match, just like
076 the perl regex modifier /.../i.
077
078 =back
079
080 =head1 DESCRIPTION
081
082 B<cgrep> reads the files provided on the command
083 line - or standard input in case there are none,
084 just like C<grep> does.
085
086 It takes a mandatory C<regex> parameter, which
087 must be a valid Perl 5 regular expression. It
088 will print out all lines matching the regex and
089 colorize the matches accordingly.
090
091 Be sure to escape regex metacharacters if they're
092 meant literally (C<|> =E<gt> C<\|>). Quote the
093 regex on the command line if it contains shell
094 metacharacters.
095
096 =head1 EXAMPLES
097
098 Print all lines in C<myfile.dat> and
099 C<yourfile.dat> containing the word I<main>
100 and colorize all occurrences of the word C<main>:
101
102 cgrep main myfile.dat yourfile.dat
103
104 Print and colorize all numbers in file C<file>:
105
106 cat file | cgrep '\b\d+\b'
107
108 Print all lines of the file C<file> and colorize
109 occurrences of the word C<word>:
110
111 cgrep word file
112
113 =head1 AUTHOR
114
115 2000, Mike Schilli <mschilli1@aol.com>
Die Zusatzmodule Getopt::Std,
Term::ANSIColor und Pod::Usage liegen
schon der Distribution von perl 5.6.0 bei -- wer noch immer
mit einer älteren Version herumgurkt, läd die beiden letzeren Schmankerln
wie immer mit der CPAN-Shell nach. Die Befehlsfolge
perl -MCPAN -eshell
cpan> install Term::ANSIColor
cpan> install Pod::Usage
installiert die Module auf der lokalen Maschine. Fröhliches greppen!
perldoc Pod::Usage und perldoc Term::ANSIColor
![]() |
Michael Schilliarbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com. |