# Werkzeuge der `grep`-Familie

"Global search for a regular expression and print out matched lines" - kurz `grep` ist das gebräuchlichste Kommando, um in Dateien nach bestimmten Mustern zu suchen. Die `grep`-Familie umfasst die drei Kommandos `egrep`, `grep` und `fgrep`. Das erste "Extended grep" (erweitertes grep) versteht "ein paar mehr" der Regulären Ausdrücke als `grep`. `fgrep` hingegen unterstützt nur eine eingeschränkte Teilmenge, womit sich die Suche vor allem in großen Dateien erheblich beschleunigen lässt.

`grep` arbeitet bei der Suche wesentlich effizienter als das in einem Editor geschehen wrde. Per Voreinstellung schreibt das Kommando alle Zeilen der Eingabe, die das gesuchte Muster enthalten, auf die Standardausgabe. Dabei kann die Eingabe beliebig viele Dateien, als auch die Standardeingabe betreffen. Zudem liefern die Kommandos der `grep`-Familie einen Rückgabewert an das Betriebssystem, was sie für die Verwendung in Shellprogrammen bevorzugt.

## Der Rückgabewert von `grep`

Rückgabewert | Bedeutung
0 | Muster wurde gefunden
1 | Muster wurde nicht gefunden
2 | Datei nicht gefunden

`# "root" sollte es eigentlich in jeder "passwd" geben user@sonne> grep root /etc/passwd >& /dev/null user@sonne> echo $? 0 # "ROOT" gibt es hoffentlich nicht user@sonne> grep ROOT /etc/passwd >& /dev/null user@sonne> echo $? 1 # die Datei "/bla" gibt es hoffentlich nicht user@sonne> grep text /bla >& /dev/null user@sonne> echo $? 2`

## Wichtige Optionen von `grep`

Option | Wirkung und Beispiel
`-c` | Anzeige der Anzahl Zeilen, in denen das Muster gefunden wurde:
`user@sonne> grep -c bash /etc/passwd 38`
`-i` | Groß- und Kleinschreibung werden nicht unterschieden:
`user@sonne> grep -i ROot /etc/passwd root:x:0:0:root:/root:/bin/bash`
`-l` | Nur Anzeige der Namen der Dateien, in denen das Muster gefunden wurde:
`user@sonne> grep -l tcp /etc/host* /etc/hosts.allow /etc/hosts.deny`
`-n` | Zeigt die Zeilennummer an, in der das Muster gefunden wurde:
`user@sonne> grep -n root /etc/passwd 1:root:x:0:0:root:/root:/bin/bash`
`-s` | Unterdrückt die Fehlerausgaben (Standardfehler); sinnvoll in Skripten.
`-v` | Zeigt alle Zeilen an, die das Muster nicht enthalten:
`# ohne diese Option user@sonne> ps ax | grep inetd 133 ? S 0:00 /usr/sbin/inetd 762 pts/2 S 0:00 grep inetd # die Ausgabe "grep" herausfiltern user@sonne> ps ax | grep inetd | grep -v grep 133 ? S 0:00 /usr/sbin/inetd`
`-w` | Das Suchmuster muss ein einzelnes Wort sein (also kein Bestandteil eines anderen Wortes).
`-A [n]` | Zeigt `Anzahl` Zeilen an, die der Zeile mit dem Muster folgen.
`user@sonne> grep -A 2 root /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/bash daemon:x:2:2:daemon:/sbin:/bin/bash`
`-B [n]` | Zeigt `Anzahl` Zeilen an, die vor der Zeile mit dem Muster liegen.

## Von `grep` unterstützte Reguläre Ausdrücke

In nachfolgender Tabelle sind alle Reguläre Ausdrücke aufgeführt, die die Kommandos der `grep`-Familie unterstützen. Ein "g" in der Spalte "?grep" besagt, dass dieser Ausdruck vom "normalen" `grep` unterstützt wird; ein "e" betrifft `egrep`. `fgrep` arbeitet wie `grep`, außer, dass alle Sonderzeichen ihre Wirkung verlieren.

Reg.Ausdruck | ?grep | Wirkung
`^` | ge | Beginn der Zeile
`$` | ge | Ende der Zeile
`.` | ge | Genau ein beliebiges Zeichen
`*` | ge | Beliebig viele des vorangegangenen Zeichens
`[]` | ge | Ein Zeichen aus dem Bereich. Anstelle von Zeichen können vordefinierte Klassen von Zeichen verwendet werden: [:alnum:], [:alpha:], [:cntrl:], [:digit:], [:graph:], [:lower:], [:print:], [:punct:], [:space:], [:upper:], und [:xdigit:].
`[^]` | ge | Kein Zeichen aus dem Bereich
`\<` | g | Muster am Wortanfang suchen
`\>` | g | Muster am Wortende suchen
`\(..)\` | g | Eingeschlossenes Muster vormerken; auf dieses kann später über `\1` zugegriffen werden. Bis zu neun Muster können auf diese Weise gespeichert werden (Beispiel).
`x\{m\}` | g | `m`-faches Auftreten des Zeichens `x`
`x\{m,n\}` | g | mindestens `m`-, maximal `n`-maliges Auftreten des Zeichens `x`
`+` | e | Mindestens ein Auftreten des vorangegangenen Zeichens
`?` | e | Höchstens ein Auftreten des vorangegangenen Zeichens
`x|y` | e | Zeichen "x" oder Zeichen "y"
`(abc|xyz)` | e | Zeichenkette "abc" oder Zeichenkette "xyz"

## Beispiele

Einfache Beispiele zur Anwendung von `grep` begegneten uns schon an mehreren Stellen dieses Buches. Nun möchte ich versuchen, anhand typischer Anforderungen bei der alltäglichen Arbeit mit einem Unix-System, die Verwendung der komplexeren Mechanismen zu erläutern.

- Bei der Systemadministration fragt man sich häufig, in welcher Datei eigentlich welche Shell-Variable gesetzt wird? Die globalen Vorgaben erfolgen zum Großteil in den Dateien des Verzeichnisses `/etc`. Also interessieren uns die Namen der Dateien, in denen z.B. die `PATH`-Variable modifiziert wird:

  `user@sonne> grep -l PATH /etc/* 2>/dev/null /etc/csh.cshrc /etc/login.defs /etc/manpath.config /etc/profile /etc/profile.rpmsave /etc/rc.config /etc/squid.conf`

  Die Umleitung der Fehlerausgabe nach `/dev/null` ist sinnvoll, da `grep` nicht auf Verzeichnisse anwendbar ist.

- Wieviele Nutzer sind Mitglied in der default-Gruppe `users` (GID 100)?

  `user@sonne> grep -c ':[0-9]\{1,\}:100:' /etc/passwd 9`

  Bei der Angabe des Suchmusters hilft uns die Kenntnis des Aufbaus der

  `user@sonne> grep -c ':[[:digit:]]\{1,\}:100:' /etc/passwd
  9`

- Welche Netzwerkdienste über UDP sind auf unserem System verfügbar (siehe Datei /etc/inetd.conf)?

  `user@sonne> grep '^[^#].*[[:space:]]udp' /etc/inetd.conf time dgram udp wait root internal talk dgram udp wait root /usr/sbin/tcpd in.talkd ntalk dgram udp wait root /usr/sbin/tcpd in.talkd rplay dgram udp wait root /usr/sbin/tcpd rplayd -t 30 -c 60 -s 16384 -F0 --inetd netbios-ns dgram udp wait root /usr/sbin/nmbd nmbd`

  Jede Zeile, die mit einem `#` beginnt, ist ein Kommentar. Also filtern wir solche Zeilen aus (`^[^#]`). Das gesuchte Protokoll ist "udp". Vor diesem Schlüsselwort können beliebige viele beliebige Zeichen (`.*`) gefolgt von einem Leerzeichen oder Tabulator (`[[:space:]]`)stehen.

- Je gezielter man nach Informationen fahndet, desto verwirrender wird die Angabe der Suchmusters. In zahlreichen Fällen wird die Verwendung von Pipes einleuchtender sein. Das Ergebnis aus obigen Beispiel erhält man auch mit folgender Befehlsfolge:

  `user@sonne> grep -w udp /etc/inetd.conf | grep -v ^# time dgram udp wait root internal talk dgram udp wait root /usr/sbin/tcpd in.talkd ntalk dgram udp wait root /usr/sbin/tcpd in.talkd rplay dgram udp wait root /usr/sbin/tcpd rplayd -t 30 -c 60 -s 16384 -F0 --inetd netbios-ns dgram udp wait root /usr/sbin/nmbd nmbd`