Leseprobe

Werbung
3
awk und sed
awk..........................................................................................................66
sed ...........................................................................................................73
Übersicht über die regulären Ausdrücke .......................................76
66
awk: Aufrufsyntax, Optionen, Struktur eines awk-Programms, Reguläre Ausdrücke
awk
Aufrufsyntax
Folgende awk-Aufrufe sind möglich:
(1) awk [optionen] 'awk-programm' d1 d2 ...
(2) awk [optionen] -f datei d1 d2 ...
Bei (1) wird das awk-Programm direkt auf der
Kommandozeile (in ´...´) angegeben und bei (2)
befindet sich das awk-Programm in datei.
d1, d2 usw. sind die Namen der zu bearbeitend. DatenDateien oder Zuweisungen der Form var=text. Eine
Zuweisung wird immer erst dann vorgenommen,
wenn alle vorherigen Argumente abgearbeitet sind.
Somit erlaubt diese Art der Zuweisung, Variablen zu
verändern, bevor und nachdem eine Datei durch das
awk-Programm bearbeitet wurde.
Optionen
-f datei
awk-Programm befindet sich in datei.
-Fregausdr
setzt die builtin-Variable FS und legt somit über
den regulären Ausdruck regausdr Trennzeichen für
Eingabefelder fest.
-- zeigt explizit das Ende der Optionen-Liste an;
muss nicht angegeben werden.
-v var=text
setzt die awk-Variable var auf wert, noch bevor die
erste Aktion des awk-Programms ausgeführt wird.
Struktur eines awk-Programms
Ein awk-Programm ist eine Folge von
pattern { Aktion }
pattern { Aktion }
::::
und Funktionsdefinitionen der Form:
function fname(param-liste) { anweisung }
Fehlt das pattern zu einer { Aktion }, so wird die
entsprechende
Aktion
für
alle
Eingabezeilen
ausgeführt.
Ist nur ein pattern, aber keine { Aktion } angegeben, so
wird jede durch pattern abgedeckte Eingabezeile
einfach ausgegeben.
pattern { Aktion }-Angaben und Funktionsdefinitionen
werden durch neue Zeilen oder Semikolons voneinander getrennt.
Pattern
Abhängig davon, ob eine pattern-Vorgabe für eine
Eingabezeile paßt, wird die zugehörige {Aktion} (falls
angegeben) ausgeführt. Es werden 6 Typen von
pattern-Vorgaben unterschieden:
BEGIN { anweisungen }
anweisungen werden einmal ausgeführt, bevor die
erste Eingabezeile verarbeitet wird.
END { anweisungen }
anweisungen werden einmal ausgeführt, nachdem
die letzte Eingabezeile verarbeitet wurde.
ausdruck { anweisungen }
anweisungen werden für jede Eingabezeile
ausgeführt, für die der ausdruck WAHR
(verschieden von 0) ist.
/regulärer ausdruck/ { anweisungen }
anweisungen werden für jede Eingabezeile
ausgeführt, welche einen String enthält, für den
der reguläre ausdruck zutrifft.
zusammengesetztes pattern { anweisungen }
Ein zusammengesetztes pattern verknüpft Ausdrücke
mit && (AND), || (OR), ! (Negation) oder (..)
(runde Klammer).
anweisungen werden für jede Eingabezeile
ausgeführt, für die das zusammengesetzte pattern
paßt.
pattern1, pattern2 { anweisungen }
pattern1,pattern2 definiert einen Bereich von der
ersten Zeile, auf die pattern1 paßt, bis einschließlich
der Zeile, auf die pattern2 paßt; die anweisungen
werden für alle Zeilen aus diesem Bereich
ausgeführt.
BEGIN und END dürfen nicht mit anderen patternTypen kombiniert werden und zu ihnen muss immer
{ Aktion } angegeben sein.
Ein Bereichspattern (pattern1,pattern2) darf nicht Teil
eines anderen pattern sein.
Reguläre Ausdrücke
siehe "Übersicht über reguläre Ausdrücke" auf Seite 76;
dort nicht erwähnt sind die möglichen
Escape-Sequenzen in regulären Audrücken:
\a
Alert, Klingelton
\b
Backspace, Zurücksetz-Zeichen
\f
Formfeed, Seitenvorschub
\n
Newline, Zeilenvorschub
\r
Return; Wagenrücklauf
\t
Tabulator
\v
Vertikaltabulator
\xhh
Zeichen mit hexadez. ASCII-Wert hh
\ddd
Zeichen mit oktalem ASCII-Wert ddd
\z
anderes Zeichen z, welches hier nicht
vorkommt; z.B. \\ für \ und \" für "
Aktionen, Eingabe mit getline, Ausgabeanweisungen, printf, sprintf
Aktionen
pattern legen fest, wann etwas für Eingabezeilen zu tun
ist, und Aktionen, die immer mit {..} zu klammern sind,
legen fest, was für Eingabezeilen zu tun ist. Sind in
einer Aktion mehrere Anweisungen angegeben, so
müssen diese durch neue Zeilen oder Semikolons
voneinander getrennt sein.
Anweisungen in Aktionen können sein:
ausdrücke, mit Konstanten, Variablen,
Zuweisungen, Funktionsaufrufen usw.
break
continue
delete array-Element
do anweisung while (ausdruck)
Ein-/Ausgabe-Anweisungen
exit bzw. exit ausdruck
for (ausdruck ; ausdruck ; ausdruck) anweisung
for (variable in array) anweisung
if (ausdruck) anweisung
if (ausdruck) anweisung else anweisung
next
[awk-Progr. sofort mit nächster Eingabezeile von Beginn an wieder starten]
while (ausdruck) anweisung
return bzw. return ausdruck
{ anweisungen }
In einer if-else Anweisung muss erste anweisung mit
Semikolon abgeschlossen oder mit { } geklammert sein,
wenn sich folgendes else in der gleichen Zeile befindet.
Ebenso muss für die do-while-Anweisung die
Schleifen-anweisung mit Semikolon abgeschlossen oder
mit { } geklammert sein, wenn sich das abschließende
while in der gleichen Zeile befindet.
Eingabe mit Funktion getline
getline liest das nächste Eingabe-Record. Als
Rückgabewert liefert getline:
1, wenn ein Record gelesen werden konnte,
0, wenn das Dateiende erreicht wurde, oder
-1, wenn beim Lesen ein Fehler auftrat.
Ausdruck
Setzt die Variable
getline
getline var
getline <dateiname
getline var <dateiname
kommando | getline
kommando | getline var
$0, NF, NR, FNR
var, NR, FNR
$0, NF
var
$0, NF
var
Direktes Lesen von der Standardeingabe ist mit
getline ......< "-" möglich.
67
Ausgabeanweisungen
print
gibt $0 auf die Standardausgabe aus
(entspricht print $0)
print ausdr1, ausdr2, ...
gibt Ausdrücke ausdr1, ausdr2, ... durch Inhalt von
OFS getrennt auf Standardausgabe aus;
abschließend wird noch der Inhalt von ORS
ausgegeben. Die print-Ausgabe kann durch
Angabe von >, >> oder | auch in eine Datei oder
eine Pipe umgelenkt werden.
printf(format, ausdruck1, ausdruck2, ...)
printf verhält sich ähnlich wie print, außer dass
bei der Ausgabe entspr. format-Angabe
Formatierung und kein automatischer
Zeilenvorschub stattfindet.
close(dateiname)
close(kommando)
schließt Verbindung zwischen einer Ausgabe und
einem dateinamen bzw. kommando.
Die Argument-Liste für printf muss nicht wie in C mit
(..) geklammert sein. Klammern sind bei print und
printf nur nötig, wenn ein Ausdruck in Arg.-Liste
einen Vergleichsoperator enthält, da sonst awk nicht
erkennen kann, ob es sich bei < oder > um
Umlenkungs- od. Vergleichsoperator handelt.
Formatieren mit printf / sprintf
printf und sprintf werden zur Formatierung benötigt.
String format kann sich dabei zusammensetzen aus:
- Zeichen (nicht %); werden so ausgegeben
- Umwandlungsvorgaben (beginnen mit %) und
beziehen sich auf die folgenden Argumente
Eine Umwandlungsvorgabe setzt sich wie folgt
zusammen: % F W G U
wobei nur Umwandlungszeichen U anzugeben ist;
F, W und G sind optional.
F = [-]; - bedeutet linksbündige Justierung.
W = [zahl]
Mindestanzahl der auszugebenden Zeichen.
Wenn Wert des entspr. Arguments weniger Stellen
als zahl hat, wird mit Leerzeich. oder mit 0 (wenn
zahl mit 0 beginnt) aufgefüllt.
G = [.zahl]
zahl legt für Strings die maximale Anzahl von
auszugebenden Zeichen und für Zahlen die
auszugebenden Nachkommastellen fest.
U = Umwandlungszeichen
c
d
e
f
g
o
s
x
%
ASCII-Zeichen
vorzeichenbehaftete ganze Dezimalzahl
Gleitpunktzahl in Form [-]d.ddddddE[+-]dd
Gleitpunktzahl in Form [-]ddd.dddddd
e- oder f-Form, abh. davon, welche kürzer
vorzeichenlose Oktalzahl
String
vorzeichenlose Hexadezimalzahl
für das Zeichen %
68
awk: Konstanten, Variablen
Konstanten
String-Konstanten
Eine String-Konstante ist eine Zeichenkette, die mit " ..
" geklammert ist, z.B.: "Hans". String-Konstanten
können auch Escape-Sequenzen enthalten, z.B.
"\b\n\"Hallo\"!\a".
Numerische Konstanten
Diese Konstantengruppe umfaßt ganze Zahlen (wie
z.B. 4700) und Gleitpunktzahlen (wie z.B. 3.14, 0.12e-4
oder 413.5E3). Verschiedene Darstellungen der
gleichen Zahl (wie z.B. 1e4 und 10000) besitzen den
gleichen Wert.
Alle Zahlen werden in Gleitpunktdarstellung
gespeichert, wobei die Genauigkeit von der
entsprechenden Maschine abhängt.
Variablen
Eine Variable hat einen String-Wert oder einen
numerischen Wert oder auch beides. Da awkVariablen nicht deklariert werden, wird der Datentyp
implizit bestimmt. Wenn notwendig, konvertiert awk
einen String in einen numerischen Wert und
umgekehrt. Eine nicht explizit initialisierte Variable hat
den Wert "" (Null-String) bzw. den numerischen Wert
0.
Es existieren 3 Arten von Variablen:
Benutzerdefinierte Variablen
Der Name einer solchen Variablen muss mit einem
Buchstaben oder Unterstrich beginnen. Die weiteren
Zeichen dürfen Buchstaben, Unterstriche oder
Ziffern sein.
Feld-Variablen
Feld-Variablen enthalten die einzelnen Felder der
jeweiligen Eingabezeile (Record). Sie können genau
wie
andere
Variablen
verwendet
werden,
insbesondere können ihnen auch neue Werte
zugewiesen werden. Die Trennzeichen für die
einzelnen Felder einer Eingabezeile sind in der
builtin-Variable FS angegeben. Ein Zugriff auf den
Wert einer Feld-Variablen erfolgt durch $ gefolgt von
der
Feld-Nummer:
$1,
$2,
...,
$NF.
$0 steht immer für die ganze Zeile (Record).
Builtin-Variablen
Variable
ARGC
Bedeutung
default
Anzahl der Argumente
in der Kommandozeile
ARGV
Array, das Argumente
aus Kommandozeile enthält
ENVIRON Array, das Werte der
Environment-Variablen enthält;
Indizes dieses Arrays sind die
Namen der Environment-Variablen
FILENAME Name der akt. Eingabedatei
FNR
(File Number of Records)
Anzahl der gelesenen Eingabezeilen in aktueller Eingabedatei
""
FS
(Field Separator)
Trennzeichen für Eingabefelder
NF
(Number of Fields)
Anzahl der Felder in der
momentanen Eingabezeile
NR
(Number of Records)
Anzahl der bisher gelesenen
Eingabezeilen (über mehrere
Eingabedateien weitergezählt)
"%.6g"
OFMT
(Output ForMaT)
Ausgabe-Format für Zahlen
""
OFS
(Output Field Separator)
Trennzeichen für Ausgabefelder
"\n"
ORS
(Output Record Separator)
Trennzeichen für Ausgabezeilen
RLENGTH Länge eines Strings, der durch
Aufruf von match ermittelt wurde
"\n"
RS
(input Record Separator)
Trennzeichen für die Eingabezeilen
RSTART
Start des Strings, der durch Aufruf
von match ermittelt wurde
"\034"
SUPSEP
(SUPscript SEParator)
Trennzeichen für die Indizes
bei mehrdimensionalen Arrays
FILENAME wird jedesmal neu gesetzt, wenn eine
neue Eingabedatei eröffnet wird.
FNR, NF und NR werden jedesmal neu gesetzt, wenn
eine neue Eingabezeile gelesen wird.
NF wird jedesmal neu gesetzt, wenn der Inhalt von $0
sich ändert oder wenn ein neues zusätzliches Feld
erzeugt wird.
RLENGTH und RSTART werden jedesmal neu
gesetzt, wenn die Funktion match aufgerufen wird.
Diese Builtin-Variablen können innerhalb von
Ausdrücken verwendet werden, und dürfen vom
Benutzer auch mit anderen Werten vorbesetzt werden.
Operatoren für Ausdrücke, Vergleiche, Arrays
Operatoren für Ausdrücke
Die Operatoren in aufsteigender Priorität sind:
= += -= *= /= %= ^=
?:
||
&&
in
~ !~
< <= == != >= >
Konkatenation
+ * / %
+ !
^ **
++ -++ -$
()
Zuweisung
Bedingungsoperator
logisches OR
logisches AND
Array-Mitglieds-Operator
Beinhaltungsoperator
relationale Operatoren
Addition, Subtraktion
Multiplikation, Division,
Modulo
Vorzeichen
logisches NOT
Potenz-Operator
Präfix-Operator
Postfix-Operator
Feld-Operator
Gruppierung
Bei gleicher Priorität werden fast alle Operatoren "von
links nach rechts" abgearbeitet: 3*4/2 entspricht
(3*4)/2 und nicht 3*(4/2).
"Von rechts nach links" werden bei gleicher Priorität
nur die Zuweisungsoperatoren, die Potenz-Operatoren
und der Bedingungsoperator abgearbeitet:
2^2^4 entspricht 2^(2^4) und nicht (2^2)^4.
Vergleiche
Wenn beide Operanden eines Vergleichsoperators
numerisch sind, wird ein numerischer Vergleich
durchgeführt (z.B. 13 > 5).
Sind beide Operanden eines Vergleichsoperators
Strings, so wird ein String-Vergleich durchgeführt
(z.B."Franz" < "Fritz" und "Hans" < "Hansi").
Ist einer der Operanden numerisch und der andere ein
String, so wird der numerische Operand in einen
String umgewandelt und ein String-Vergleich
durchgeführt (z.B. 3 > "13").
Vorsicht ist geboten bei Werten, welche zu groß sind,
um numerisch dargestellt zu werden; so kann z.B. 10 <
1e5000 einen String-Vergleich nach sich ziehen.
Umwandlungen können durch einen kleinen "Trick"
erzwungen werden:
zahl ""
(Anhängen eines Null-Strings an eine
numerische Zahl; somit wird eine Umwandlung der
Zahl in einen String erzwungen)
string+0 (Aufaddieren von 0 auf einen String, um so
die Umwandlung eines Strings in eine Zahl zu
erzwingen)
Der numerische Wert eines Strings ist der Wert des
längsten Präfixes, das numerisch interpretierbar ist.
69
Arrays
Eindimensionale Arrays
Strings und Zahlen können in eindimensionalen
Arrays gespeichert werden. Arrays müssen weder
explizit deklariert werden noch muss ihre Größe im
voraus angegeben werden. Wie Variable so werden
auch Array-Elemente eingeführt, indem man einfach
auf sie mit
arrayname[index]
zugreift. Sie sind mit 0 bzw. "" initialisiert.
Assoziative Arrays
Im Unterschied zu Arrays in Programmiersprachen
sind bei awk Strings als Indizes erlaubt; solche Arrays
mit String-Indizierung werden assoziative Arrays
genannt.
for-in-Schleife
awk bietet eine for-Schleife an, um alle Indizes eines
Arrays mit einer variable zu durchlaufen:
for (variable in array)
anweisung
Die Reihenfolge, in der die Indizes durchlaufen
werden, ist implementationsabhängig.
in-Operator
Der in-Operator kann auch dazu verwendet werden,
um festzustellen, ob ein bestimmter Index in einem
Array vorkommt oder nicht:
index in array
Dieser Ausdruck liefert WAHR (Wert 1), wenn
array[index] existiert, sonst FALSCH (Wert 0).
Array-Elemente mit delete löschen
Ein Array-Element kann mit
delete array[index]
aus einem Array entfernt werden.
Mehrdimensionale Arrays
awk unterstützt nicht direkt mehrdim. Arrays, aber es
ist möglich, mehrdim. Indizes wie i,j oder x,y,z
anzugeben. awk konkateniert zuerst diese Indizes (mit
Trennzeichen dazwischen) und faßt dann solche
mehrdim. Index-Angaben als einen eindim. Index auf.
Wenn geprüft werden soll, ob ein mehrdim. Index in
einem Array vorkommt, so ist der mehrdim. Index in
Klammern anzugeben:
if ((i,j) in array)
Um ein "mehrdim. Array" mit einer Schleife zu
durchlaufen, können die Konstruktionen
(1) for ((i,j) in array)
oder
(2) for (m in array)
verwendet werden. Werden bei (2) in Schleife einzelne
Indizes benötigt, erhält man sie mit
split(m, a, SUPSEP)
Der 1. Index liegt dann in a[1], der 2. in a[2] usw.
Array-Elemente können nie wieder Arrays sein.
70
Funktionen
Es existieren folgende Arten von Funktionen:
Benutzerdefinierte Funktionen
Hierfür gilt die folgende Syntax:
function funktionsname(parameter-liste) {
anweisungen
}
Eine Funktionsdefinition kann überall dort angegeben
werden, wo pattern { aktion }-Konstrukte erlaubt sind.
In einer Funktionsdef. sind Zeilenvorschübe nach {
und vor } nicht unbedingt gefordert.
Die Parameterliste ist eine Folge von Variablennamen,
welche durch Kommas getrennt sind. Werden diese
Namen in der Funktion verwendet, so beziehen sie
sich auf die Parameter und nicht auf gleichnamige
globale Variablen.
Benutzerdef. Funktionen sind "typenlos"; die gleiche
Funktion kann sowohl ganze Zahlen, Strings als auch
Gleitpunktzahlen an den aufrufenden Programmteil
zurückgeben.
Die Angabe einer return-Anweisung in einem
Funktionskörper bewirkt das unmittelbare Verlassen
der Funktion. Soll die Funktion einen Wert an den
Aufrufer zurückgeben, dann ist
return ausdruck
anzugeben.
Bei der Übergabe von einfachen Variablen als
Parameter an eine Funktion wird nur eine Kopie des
Variablenwerts übergeben und nicht die Variable
selbst (call-by-value).
Werden Arrays als Parameter übergeben, so findet
jedoch ein call-by-reference statt.
Eine benutzereigene Funktion kann in jedem
beliebigen Ausdruck in einer
pattern { aktion }Anweisung aufgerufen werden.
Eine Funktion kann auch im Funktionskörper oder
rekursiv aufgerufen werden.
Werden in einer Funktionsdefinition neue Variablen
eingeführt, dann sind diese global, d.h. sie sind im
ganzen Programm verfügbar.
Benötigt man funktionslokale Variablen, so sind diese
am Ende der Parameterliste bei der Funktionsdef.
anzugeben, da gilt:
Wird eine Funktion mit weniger Parametern
aufgerufen, als bei der Funktionsdefinition vereinbart
wurden, so werden die restlichen Parameter als
funktionslokale Variablen eingeführt, welche mit ""
initialisiert werden.
awk: Funktionen
Arithmetische Builtin-Funktionen
Funktion
Rückgabewert
atan2(y,x)
cos(x)
exp(x)
int(x)
log(x)
rand()
sin(x)
sqrt(x)
srand(x)
Arcustangens von y/x in [-π,π]
Cosinus von x (x im Bogenmaß)
Exponentialfunktion: ex
ganzzahliger Anteil von x
natürlicher Logarithmus von x: ln x
Zufallszahl r, für die gilt: 0 ≤ r < 1
Sinus von x (x im Bogenmaß)
Quadratwurzel von x: √x
neuen Startwert x für Zufallszahlenfolge
setzen; Aufruf von srand() setzt aktuelle
Zeit als Startwert
Builtin-Stringfunktionen
gsub(r,s)
ersetzt überall in $0 r durch s;
liefert die Anzahl der Ersetzungen
gsub(r,s,t) ersetzt überall im String t r durch s; liefert
die Anzahl der Ersetzungen
index(s,t) sucht 1.Vorkommen des Strings t im String
s; liefert, wenn gefunden, Anfangsposition
von t, sonst 0
length(s) bestimmt Länge des Strings s; liefert
Anzahl der Zeichen im String s
match(s,r) prüft, ob s einen Teilstring r enthält, und
liefert Anfangspos. des Teilstrings, der r
entspricht, sonst 0; zusätzl. werden builtinVar. RSTART und RLENGTH gesetzt.
split(s,a) zerlegt s entspr. Trennzeichen in FS in
einzelne Felder und legt diese im Array a
ab; liefert Anzahl der Felder
split(s,a,tr) zerlegt s entspr. Trennzeichen in tr in
einzelne Felder und legt diese im Array a
ab; liefert Anzahl der Felder
sprintf(fmt, ausdr-liste)
formatiert Werte der in ausdr-liste
angegeb. Ausdrücke entspr. fmt; liefert
formatierten String
sub(r,s)
ersetzt in $0 den am weitesten links
stehenden Teilstring r durch s;
liefert Anzahl der Ersetzungen
sub(r,s,t) ersetzt in t den am weitesten links
stehenden Teilstring r durch s;
liefert Anzahl der Ersetzungen
substr(s,p) liefert ab Pos. p Rest von String s
substr(s,p,n) liefert ab Pos. p n Zeichen aus s
tolower(s) liefert String s, wobei alle Groß- durch
Kleinbuchstaben ersetzt sind
toupper(s) liefert String s, wobei alle Klein- durch
Großbuchstaben ersetzt sind
r reg. Ausdr. (als String oder in / .. / angegeb.)
s,t für einen String-Ausdruck
n,p für eine ganze Zahl
Wird bei gsub und sub ein & in s angegeben, so wird
es durch von r abgedeckten String ersetzt.
Mehrzeilen-Records, system, Kommandozeilen-Argumente, awk-Programme in Shell-Skripten
Mehrzeilen-Records
Der voreingestellte Wert der Builtin-Variablen RS
(Trennzeichen für Eingabe-Records) ist "\n".
Sollen jedoch mehrere Eingabezeilen als ein Record
eingelesen werden, so ist RS mit dem Null-String zu
besetzen:
BEGIN { RS = "" }
Dies bewirkt, dass Eingabe-Records durch eine oder
mehrere Leerzeilen getrennt werden und somit ein
Record aus mehreren Zeilen bestehen kann.
Unabhängig davon, wie FS besetzt ist, werden neue
Zeilen in diesem Fall immer als Feld-Trennzeichen
betrachtet.
Die Funktion system
Die Builtin-Funktion
system(ausdruck)
führt das Kommando aus, welches durch den aus
ausdruck resultierenden String bezeichnet wird. system
liefert als Rückgabewert den Status-Wert des
ausgeführten Kommandos.
Kommandozeilen-Argumente
Die Kommandozeilen-Argumente für ein awkProgramm werden in dem builtin-Array ARGV
hinterlegt. Der Wert der builtin-Variablen ARGC
liefert die Zahl von übergebenen KommandozeilenArgumenten, wobei das erste Argument in ARGV[0],
das zweite in ARGV[1] usw. abgelegt wird, so dass die
übergebenen
Argumente
in
ARGV[0]
bis
ARGV[ARGC-1] vorliegen.
Der awk-Programmname "awk" wird immer in
ARGV[0] abgelegt, und weder die Option -f noch der
Programmname werden als Argument betrachtet.
Dasselbe gilt auch für eine evtl. Option -F oder -V und
die danach angegebenen Argumente. Ebenso wird ein
direkt auf der Kommandozeile in ´....´ angegebenes
awk-Programm nicht als Argument interpretiert.
Die Argumente in ARGV dürfen modifiziert werden,
und es ist auch möglich, ARGV zu erweitern und neue
Argumente an ARGV anzuhängen. Ebenso darf
ARGC verändert werden. Wenn eine Eingabedatei
vollständig abgearbeitet ist, dann nimmt awk den
nächsten Nicht-Null-String aus ARGV (bis ARGC-1)
als nächste Eingabedatei. So kann ein Array-Element in
ARGV auf leer gesetzt werden, um zu verhindern,
dass es als Eingabedatei behandelt wird.
Der Name "-" in ARGV steht für Standardeingabe.
71
awk-Programme in Shellskripten
awk-Programme können direkt in Shellskripten
eingebettet werden. Die einfachste Möglichkeit, Werte
aus einem Shellskript an ein awk-Programm zu
übergeben, ist die Übergabe der Werte über ARGV. In
einem Shellskript kann
awk 'awk-prog' kdo_zeilen_argumente
angegeben werden.
Eine andere Möglichkeit, Werte aus einem Shellskript
an ein awk-Programm zu übergeben, ist die Übergabe
dieser Werte über eine Pipe.
Zugriff auf Shell-Variable in awk
Es gibt mehrere Möglichkeiten, in einem awkProgramm auf Shell-Variablen zuzugreifen:
1. Shell-Parameter mit ' .. ' klammern
In diesem Fall ist nur zu unterscheiden, ob der Wert
der Variable im awk-Programm als numerischer
Wert (genügt die alleinige Klammerung ' .. ') oder als
String (muss dann in "' .. '" eingebettet sein) zu
interpretieren ist.
2. awk-Programm mit " .. " klammern
Auf Shell-Variablen innerhalb eines awk-Programms
kann dann mit $0, $1, ... zugegriffen werden. Auf die
awk-Felder muss dann mit \\$0, \\$1, ...
zugegriffen werden.
3. Shell-Variablen über Zuweisungen in awkKommandozeile übergeben
4. Shell-Variablen mit "' .. '" klammern
Kommandosubstitution in awk
Um Kommandosubstitution in einem awk-Programm
durchzuführen, ist '"`kommando`"' anzugeben.
Direkte Ausführung von awk
Seit System V.4 können awk-Programme selbst
ausführbar gemacht werden, indem im awkProgramm in der ersten Zeile folgendes angegeben
wird:
#! awk -f
oder
#! /usr/bin/awk -f
Ist diese Angabe in einem awk-Programm enthalten, so
kann man anstelle von
awk -f awk-programm
auch nur
awk-programm
aufrufen.
72
awk: Beispiel
Beispiel für ein awk-Programm
Das folgende awk-Programm scheck.awk druckt automatisch Schecks aus:
BEGIN {
FS="\t+"
striche = "------------------------------------------------------------"
split("Januar Februar Maerz April Mai Juni " \
"Juli August September Oktober November Dezember", monat, " ")
split("ein zwei drei vier fuenf sechs sieben acht neun zehn " \
"elf zwoelf dreizehn vierzehn fuenfzehn " \
"sechzehn siebzehn achtzehn neunzehn ", biszwanzig, " ")
split("zehn zwanzig dreissig vierzig fuenfzig " \
"sechzig siebzig achtzig neunzig ", zehner, " ")
"date +%D" | getline datum
}
NF != 3 { printf("Zeile %d, 3 Felder pro Zeile erforderlich: %s\n", NR, $0); f++; next; }
$2 < 0 { printf("Zeile %d, Betrag muss positiv sein: %s\n", NR, $0);
f++; next; }
$1 !~ /[0-9]+/ { printf("Zeile %d, Fuer Schecknummer nur Ziffer erlaubt: %s\n", NR, $0)
f++; next;
}
$2 !~ /[0-9]*[,][0-9]*/ { printf("Zeile %d, Unerlaubter Betrag: %s\n", NR, $0)
f++; next;
}
$2 ~ /[,][0-9][0-9][0-9]+/ {
printf("Zeile %d, Warnung: Nur 2 Nachkommast. relevant: %s\n", NR, $0) }
{ komma = index($2, ",")
nachkomma = sprintf("%-2.2s", substr($2, komma+1) "00")
ganz = substr($2, 1, komma-1)
split(datum, dat, "/")
printf("\n%s\n", striche)
printf("%40s.%s.%s\n\n", dat[2], monat[dat[1]+0], dat[3])
printf("\tZahlen Sie gegen diesen Scheck --DM%s%s---\n\n",
substr(striche, 1, 12-length(ganz)-3), ganz "," nachkomma)
inworte = in_woerter(ganz)
if (substr(inworte, length(inworte)-3) == "ein ")
inworte = substr(inworte, 1, length(inworte)-4) "eine "
inworte = inworte "Mark und " nachkomma " Pfennig"
printf("In Worten: --")
while (length(inworte)>45) {
pos = rindex(substr(inworte,1,45), " "); printf("%s\n", substr(inworte, 1, pos-1))
inworte = substr(inworte, pos+1);
printf("%13s", " ")
}
printf("%s--\n\n", inworte);
printf("an: %s\n\n\n", $3)
printf("\t\tScheck-Nr.: %s\n\n%s\n\n", $1, striche)
}
END { if (f) print "Fehler in Eingabe:"
print f
}
function in_woerter(zahl) {
zahl = int(zahl)
if (zahl>=1000) return in_woerter(zahl/1000) "Tausend " in_woerter(zahl%1000)
if (zahl>=100) return in_woerter(zahl/100) "Hundert " in_woerter(zahl%100)
if (zahl>=20)
if (zahl%10) return biszwanzig[zahl%10] " und " zehner[int(zahl/10)] " "
else
return zehner[int(zahl/10)] " "
return biszwanzig[zahl] " "
}
function rindex(string, such) {
for (i=length(string) ; i>=1 && substr(string,i,1)!=such ; i--) ;
return i
}
Wenn diesem awk-Programm folgende Eingabedatei scheck.ein vorgelegt würde:
1234567890
5462165365
12,7
854765,21
Fa. Holzwurm
Fa. Gross
in der je Zeile folgendes angegeben ist:
Scheck-Nummer
Betrag
Empfaenger
und ruft man es mit folgender Kommandozeile auf: awk -f scheck.awk scheck.ein, so gibt es folgendes aus:
-----------------------------------------------------------01.Juli.99
Zahlen Sie gegen diesen Scheck --DM-------12,70--In Worten: --zwoelf Mark und 70 Pfennig-an: Fa. Holzwurm
Scheck-Nr.: 1234567890
----------------------------------------------------------------------------------------------------------------------01.Juli.99
Zahlen Sie gegen diesen Scheck --DM---854765,21--In Worten: --acht Hundert vier und fuenfzig Tausend
sieben Hundert fuenf und sechzig Mark und 21
Pfennig-an: Fa. Gross
Scheck-Nr.: 5462165365
------------------------------------------------------------
sed
73
sed
Aufrufsyntax
Die vollständige Aufrufsyntax für den Stream-Editor
sed ist:
sed [-n] [-e skript] [-f skript-datei ] [datei(en)]
sed bearbeitet die angegebenen Eingabe-dateien oder,
falls keine Eingabedatei angegeben ist, die Daten der
Standardeingabe und schreibt das Ergebnis auf die
Standardausgabe.
Die Editieranweisungen für die Eingabedateien
werden sed durch ein sed-Skript vorgegeben.
Optionen
-e skript
skript sind die durchzuführenden Editieranweisungen. Wenn skript Shell-Sonderzeichen enthält, so
ist -e 'skript' anzugeben. Wenn nur ein -e skript und
keine -f skript-datei-Angabe in der Kommandozeile
vorkommt, so kann -e auch weggelassen werden.
-f skript-datei
Die durchzuführenden Editieranweisungen befinden sich in der Datei skript-datei.
-n
bewirkt, dass die automatische Ausgabe von
bearbeiteten Eingabezeilen auf stdout unterdrückt
wird. Es wird nur noch das auf Standardausg.
geschrieben, was mittels expliziter Ausgabeanweisung (wie z.B. print) in den sed-Anweisungen vorgegeben wird.
Beim Aufruf dürfen mehrere Skripten mit -e und -f
(auch kombiniert) angegeben werden.
sed-Editieranweisungen
Die allgemeine Form einer sed-Editieranweisung ist:
[adresse1 [,adresse2]] funktion [argumente]
Durch adresse1 und adresse2 wird ein bestimmter
Bereich von Zeilen spezifiziert. Allerdings ist die
Angabe der Adressen nicht gefordert. Folgendes gilt:
keine Adressen angegeben:
alle Eingabezeilen
Nur eine Adresse angegeben:
alle Eingabezeilen, auf die die angegebene Adresse
zutrifft
Beide Adressen angegeben:
Bereich (von, bis) von Eingabezeilen
Auf diese so ausgewählten Zeilen wird dann die
Editieraktion funktion ausgeführt. Diese Funktion kann
dann weitere argumente besitzen.
Adressen
Die Adreßangaben adresse1 und adresse2 wählen aus
den Eingabedateien bestimmte Zeilen aus. Folgende
Angaben sind für adresse möglich:
ganze Zahl n
adressiert die Zeile n; es ist zu beachten, dass die
Zeilennumerierung bei mehreren Eingabedateien
nicht ständig neu beginnt, sondern weitergezählt
wird.
$
adressiert die letzte Eingabezeile.
/regulärer Ausdruck/
adressiert Eingabezeilen, welche einen String
beinhalten, der durch den vorgegebenen regulären
Ausdruck abgedeckt ist.
Reguläre Ausdrücke
siehe "Übersicht über reguläre Ausdrücke" auf Seite 76;
dort nicht erwähnt ist die Angabe von:
\n
für Neuezeile-Zeichen.
sed-Skripten in Shellskripten
sed-Skripten können in Shellskripten eingebettet
werden. Die einfachste Möglichkeit, Werte aus einem
Shellskript an ein sed-Skript zu übergeben, ist die
Übergabe der Werte über die Kommandozeile. In
einem Shellskript kann
sed 'sed-Skript' kdo_zeilen_argumente
angegeben werden.
Eine andere Möglichkeit, Werte von einem Programm
an ein sed-Skript weiterzureichen, ist die Übergabe
dieser Werte über eine Pipe.
Zugriff auf Shell-Variable in sed
Es gibt mehrere Möglichkeiten, innerhalb eines sedSkripts auf Shell-Variablen zuzugreifen:
1. Shell-Variable mit '.. ' klammern
In einem sed-Skript bewirkt die Klammerung einer
Shell-Variablen mit Apostroph ('.. '$variable' .. '),
dass die verschiedenen Sonderzeichen, die für sed
gelten, vor einer Interpretation durch die Shell
geschützt werden; andererseits bleibt $variable
ungeschützt und wird durch die Shell ausgewertet.
2. sed-Skript mit ".. " klammern
Eine Klammerung mit " .. " bewirkt, dass Zugriff auf
eine Shell-Variable mit $ durch die Shell interpretiert
wird, da innerh. von "..." die Sonderbedeutung von $
für die Shell nicht ausgeschaltet wird.
Bei dieser Möglichkeit ist zu beachten, dass den
Sonderzeichen (wie $ für Zeilenende), die auch in
der Shell vorhanden sind, ein \ vorangestellt werden
muss, um sie vor der Interpretation durch die Shell
zu schützen.
74
sed: Funktionen
Funktionen
In einer sed-Anweisung muss immer eine Funktion
angegeben werden. Folgende Tabelle enthält alle
Funktionen; dabei sind am Ende jeder Funktionsbeschreibung in Klammern die maximal erlaubten
Adressen angegeben:
a\
text (append) text nach Eingabezeile auf stdout
schreiben. Besteht text aus mehreren Zeilen,
so ist das Fortsetzungszeichen \ am Ende vor
CR anzugeben
(1)
b [marke] (branch) zu marke springen. Fehlt marke,
so wird neue Eingabezeile gelesen und sedSkript von Beginn an wieder ausgeführt
(2)
c\
text (change) Inhalt des Eingabepuffers durch text
ersetzen. Besteht text aus mehreren Zeilen, so
ist das Fortsetzungszeichen \ am Ende vor CR
anzugeben
(2)
d
(delete) Eingabepuffer löschen, nächste
Eingabezeile lesen und sed-Skript wieder von
Beginn an ausführen
(2)
D
(Delete first part of pattern space)
Den 1. Teil des Eingabepuffers (bis erstes \n)
löschen und sed-Skript von Beginn an wieder
mit restlichem Eingabepuffer ausführen
(2)
g
(get contents of hold area) Eingabepuffer durch
Haltepuffer-Inhalt überschreiben
(2)
G
(Get contents of hold area) Haltepuffer am
Eingabepuffer (mit \n getrennt) anhängen
(2)
h
(hold pattern space) Haltepuffer durch
Eingabepuffer-Inhalt überschreiben
(2)
H
(Hold pattern space) Eingabepuffer am
Haltepuffer (mit \n getrennt ) anhängen
(2)
i\
text (insert lines) text vor Eingabezeile auf stdout
schreiben. Besteht text aus mehreren Zeilen, so
ist das Fortsetzungszeichen \ am Ende vor
CR anzugeben
(1)
l
(list) Eingabepuffer auf stdout schreiben;
dabei nicht druckbare Zeichen als ASCII-Wert
(2-Ziffer) und überlange Zeilen als mehrere
einzelne Zeilen ausgeben
(2)
n
(next line) Eingabepuffer auf stdout schreiben
und nächste Eingabezeile in Eingabepuffer
lesen
(2)
N
(Next line) Nächste Eingabezeile an
Eingabepuffer (mit \n getrennt) anhängen;
Zeilennummer wird hier weitergezählt
(2)
(2)
p
(print) Eingabepuffer auf stdout ausgeben
P
(Print first part of the pattern space)
Den 1. Teil des Eingabepuffers (bis
einschließl. erstem \n) auf stdout schreiben
(2)
q
(quit) Nach Ausgabe des Eingabepuffers
Skriptausführung beenden
(1)
r datei (read file) datei auf stdout ausgeben und
nächste Eingabezeile lesen
(2)
s/regulärer Ausdruck/text/[flags] (substitute)
Im Eingabepuffer Strings, die regulärer
Ausdruck abdeckt, durch text ersetzen. Für
flags kann Folg. angegeben werden:
n
nur n-tes Textstück ersetzen
g
alle Textstücke ersetzen
p
falls Ersetzung stattfand,
verändert. Eing.puffer ausgeben
w datei falls Ersetzung stattfand, dann
veränderten Eingabepuffer ans
Ende von datei schreiben
Fehlen flags, wird nur erste Textstück des
Eingabepuffers ersetzt.
Bei mehreren flags-Angaben muss g (falls
verwendet) an 1. Stelle stehen.
Wird in text das Zeichen & angegeben, so
wird dafür der durch regulären Ausdruck
abgedeckte String eingesetzt
(2)
t [marke] (test substitutions)
Falls seit letztem Lesen einer Eing.zeile
oder Ausführung einer t-Funktion eine
Ersetzung stattfand, dann zu marke
springen und dort mit Abarbeitung des
Skripts fortfahren; ansonsten hat diese
Funktion keine Wirkung. Fehlt marke, so
wird ans Skriptende gesprungen
(2)
w datei (write to a file) Eingabepuffer ans Ende der
Datei datei schreiben
(2)
x
(exchange)
Eingabe- und Haltepuffer vertauschen (2)
y/string1/string2/ Im Eingabepuffer alle
Zeichen, die in string1 vorkommen,
durch die an gleicher Position in
string2 stehenden Zeichen ersetzen
(2)
! funktion
funktion (oder Funktionsgruppe bei
Klammerung mit { }) nur für die Zeilen
ausführen, für die angegebene
(2)
Adreßangabe nicht zutrifft
: marke Sprungmarke für b und t definieren
(0)
=
Zeilennr. auf stdout schreiben
(1)
{ .. }
Klammert Gruppe von Funktionen,
die nur ausgeführt werden, wenn
Eingabepuffer gefüllt ist
(2)
#
Wenn als erstes Zeichen in Zeile
angegeben, leitet dieses Zeichen einen
Kommentar ein. Ist #n angegeben, wird
automat. Ausgabe ausgeschaltet (wie bei
Option -n); Rest der Zeile nach #n wird
ignoriert. Eine Skript-Datei muss mind.
eine Nicht-Kommentarzeile enthalten
(0)
leere Funktion leere Funktion wird ignoriert
(0)
Beispiel zu sed
75
Beispiel zu sed
Das hier vorgestellte Shellskript xref erstellt mit Hilfe des sed eine einfache Cross-Reference-Liste für C-Module.
Das Shellskript xref (Statt \t muss die Tabtaste bei der Eingabe dieses Skripts gedrückt werden) besteht aus einer
"großen Pipe", die sich vom Anfang bis zum Ende des Skripts erstreckt. Jedes Kommando erfüllt eine bestimmte
Aufgabe, bevor es die so manipulierten Daten wieder über die "große Pipe" ans nächste Kommando weiterreicht:
# xref datei1 ...
#
Ausgabe einer einfachen Cross-Reference-Liste fuer die als Argumente angegebenen C-Programmdateien
ueberschrift="====D_NAME===="
# Erzeugen einer Ueberschrift (mit "====D_NAME====" geklammerter
# jeweiliger Dateiname); diese Ueberschrift wird zusammen mit dem
# zugehoerigen Inhalt jeder angegebenen Datei ueber eine Pipe
# an das nachfolgende sed-Skript weitergeleitet.
for i in $*
do
echo "$ueberschrift $i $ueberschrift"
cat $i
done |
# Das sed-Skript ersetzt dann alle einzeiligen Kommentarzeilen durch
# Leerzeilen und leitet die so geaenderten Eingabedaten ueber
# eine Pipe an ein awk-Programm weiter.
sed 's+/\*.*\*/++' |
# Das awk-Programm ersetzt mehrzeilige Kommentarzeilen durch
# Leerzeilen und leitet die so geaenderten Eingabedaten ueber
# eine Pipe an ein weiteres sed-Skript weiter.
awk '/\/\*/,/\*\// { printf("\n"); next }
{ print $0 }' |
# Das sed-Skript ersetzt alle fuer C-Namen nicht erlaubten Zeichen
# durch ein Leerzeichen; diese Ersetzung wird nur fuer Zeilen
# vorgenommen, die nicht mit "====D_NAME====" beginnen.
# Die so geaenderten Eingabedaten werden ueber eine Pipe an ein
# weiteres awk-Programm weitergeleitet.
sed '/'$ueberschrift' /!s/[^A-Za-z_0-9]/ /g' |
# Wenn dieses awk-Programm eine "Ueberschriftszeile"
# (Zeile, welche als 1. und als 3. Feld $ueberschrift enthaelt) findet,
# so haelt es den zugehoerigen Dateinamen in der Variablen DATEI
# fest und setzt die Zeilennumerierung auf 1.
# Fuer jede "echte Zeile" gibt es dann pro Feld den Feldnamen,
# den Inhalt der Variable DATEI und die Zeilennummer aus; diese
# Ausgabe wird wieder ueber eine Pipe an ein sed-Skript weitergeleitet.
awk 'BEGIN { OFS="\t" }
$1 ~ /^'$ueberschrift'$/ && $3 ~ /^'$ueberschrift'$/ {
DATEI = $2
zeil_nr = 1
next
}
{ for (i=1; i<=NF ; i++)
print $i, DATEI, zeil_nr
zeil_nr++
}' |
# Dieses sed-Skript entfernt alle Zeilen, die nicht einen erlaubten
# C-Namen besitzen; die so geaenderten Eingabedaten werden ueber
# eine Pipe an ein sort-Kommando weitergeleitet.
sed '/^[A-Za-z_][A-Za-z_0-9]*/!d'
|
# Sortieren nach den Sortierschluesseln C-Name (1. Feld),
# Dateiname (2. Feld) und Zeilennummer (3. Feld), wobei doppelte
# Zeilen geloescht werden (Option -u). Die sortierte Ausgabe wird
# ueber eine Pipe an ein awk-Programm weitergeleitet.
sort -u +0 -1 +1 -2 +2n -3
|
# (1) Falls das betrachtete Wort dasselbe ist wie das vorhergehende
#
und falls dieses Wort in derselben Datei vorkam, dann wird
#
nur die neue Zeilennummer an die vorherige Ausgabe angehaengt.
# (2) Falls dasselbe Wort in einer anderen Datei vorkam, wird (in
#
einer eigenen Zeile eingerueckt) die neue Datei mit der
#
entsprechenden Zeilennummer ausgegeben.
# (3) Falls das betrachtete Wort nicht identisch ist mit dem vorherigen,
#
werden in einer neuen Zeile das Wort, der Dateiname und die
#
Zeilennummer ausgegeben. Der neue C-Name wird in der Variablen
#
name_vorher und der Dateiname in der Variablen datei_vorher
#
abgelegt.
awk '
{
if ($1 == name_vorher) {
if ($2 == datei_vorher) {
printf(", %d", $3)
# (1)
} else {
printf("\n%-25s %15s %d", " ", $2, $3) # (2)
datei_vorher = $2
#
}
} else {
printf("\n%-25s %15s %d", $1, $2, $3)
# (3)
name_vorher = $1
#
datei_vorher = $2
#
}
}
END { printf("\n") }'
Dieses Skript xref ermittelt alle Strings, die nach Definition erlaubte C-Namen sind. Dadurch werden auch
Schlüsselwörter – wie char, while, do usw. – oder in printf angegebene Wörter aufgelistet. Um die Einbeziehung
von Konstrukten wie diese zu unterbinden, müßte ein wesentlich intelligenteres Skript geschrieben werden. Für
den Hausgebrauch sollte allerdings diese Skriptversion ausreichen.
76
Übersicht über die regulären Ausdrücke
Übersicht über die regulären Ausdrücke
Zusätzlich zu awk und sed sind in der folgenden Tabelle noch die UNIX-Tools angegeben, für die die
entsprechenden regulären Ausdrücke auch noch erlaubt sind:
deckt ab /
bewirkt
DateinamenExpandierung
awk
sed
egrep
ed, grep,
csplit, pg
ex, vi
emacs
ein beliebiges Zeichen
?
.
.
.
beliebige Zeichenkette
*
.*
.*
.*
keine, eine oder mehrmalige
Wiederholung
--
*
*
*
eine oder mehrmalige Wiederholung
--
+
\{1,\}
+
keine oder eine Wiederholung
--
?
\{0,1\}
?
n-malige Wiederholung
--
--
\{n\}
--
n- bis m-malige Wiederholung
--
--
\{n,m\}
--
--
--
\{n,\}
--
Klasse von Zeichen
[...]
[...]
[...]
[...]
Komplement-Klasse von Zeichen
[!...]
[^...]
[^...]
[^...]
Zeilenanfang
--
^RA
^RA
^RA
Zeilenende
--
RA$
RA$
RA$
Wortanfang
wort*
--
--
\<RA
Wortende
*wort
--
--
RA\>
--
RA1|RA2
--
--
mindestens n-malige Wiederholung
a oder b (Alternation)
\runde Klammern
--
--
\(RA\)
\(RA\)
n-ter Teilausdruck
--
--
\n
\n
RA, RA1, RA2 stehen für reguläre Ausdrücke
Zu awk und egrep sei noch folgendes angemerkt:
• runde Klammern:
(r) deckt den gleichen String wie r ab; um vorgegebene Prioritäten aufzuheben
•
Die Priorität der Operatoren (in aufsteigender Folge):
|
Konkatenation
* + ?
(besitzen untereinander gleiche Priorität)
()
(besitzen untereinander gleiche Priorität)
•
Die Operatoren *, + und ? beziehen sich immer auf das vorhergehende Zeichen;
sollen sie sich auf einen längeren Ausdruck beziehen, so ist dieser mit ( .. ) zu
klammern. Um runde Klammern in einem Text abzudecken, ist deren
Sonderbedeutung mit \ auszuschalten: \( bzw. \). Die Alternation kann bei
egrep auch durch ein Neuezeile-Zeichen (Carriage-Return) angegeben werden.
Herunterladen