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 grep
s 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. |