GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Bearbeiten von Text in der Befehlszeile mit grep

Stellen Sie sich vor, Sie haben eine Datei (oder mehrere Dateien) und möchten in diesen Dateien nach einer bestimmten Zeichenfolge oder Konfigurationseinstellung suchen. Jede Datei einzeln zu öffnen und zu versuchen, die bestimmte Zeichenfolge zu finden, wäre mühsam und wahrscheinlich nicht der richtige Ansatz. Was können wir also verwenden?

Es gibt viele Tools, die wir in *nix-basierten Systemen verwenden können, um Text zu finden und zu manipulieren. In diesem Artikel behandeln wir den grep Befehl, um nach Mustern zu suchen, unabhängig davon, ob sie in Dateien gefunden wurden oder aus einem Stream stammen (eine Datei oder eine Eingabe, die aus einer Pipe stammt, oder | ). In einem kommenden Artikel werden wir auch sehen, wie man sed verwendet (Stream-Editor), um einen Stream zu bearbeiten.

Der beste Weg, die Funktionsweise eines Programms oder Dienstprogramms zu verstehen, besteht darin, seine Manpage zu konsultieren. Viele (wenn nicht alle) Unix-Tools stellen während der Installation Handbuchseiten bereit. Auf Red Hat Enterprise Linux-basierten Systemen können wir Folgendes ausführen, um grep aufzulisten Dokumentationsdateien von :

$ rpm -qd grep
/usr/share/doc/grep/AUTHORS
/usr/share/doc/grep/NEWS
/usr/share/doc/grep/README
/usr/share/doc/grep/THANKS
/usr/share/doc/grep/TODO
/usr/share/info/grep.info.gz
/usr/share/man/man1/egrep.1.gz
/usr/share/man/man1/fgrep.1.gz

Da uns Manpages zur Verfügung stehen, können wir jetzt grep verwenden und erkunden Sie die Optionen.

grep Grundlagen

In diesem Teil des Artikels verwenden wir die words Datei, die Sie an folgendem Ort finden:

$ ls -l /usr/share/dict/words 
lrwxrwxrwx. 1 root root 11 Feb  3  2019 /usr/share/dict/words -> linux.words

Diese Datei enthält 479.826 Wörter und wird von words bereitgestellt Paket. In meinem Fedora-System ist dieses Paket words-3.0-33.fc30.noarch . Wenn wir den Inhalt der words auflisten Datei sehen wir die folgende Ausgabe:

$ cat /usr/share/dict/words
1080
10-point
10th
11-point
[……]
[……] 
zyzzyva
zyzzyvas
ZZ
Zz
zZt
ZZZ

Ok, also haben wir die words gesagt Die Datei enthielt 479.826 Zeilen, aber woher wissen wir das? Denken Sie daran, dass wir vorhin über Manpages gesprochen haben. Mal sehen, ob grep bietet eine Option zum Zählen von Zeilen in einer bestimmten Datei.

Ironischerweise verwenden wir grep wie folgt nach der Option suchen:

Wir brauchen also offensichtlich -c , oder die lange Option --count , um die Anzahl der Zeilen in einer bestimmten Datei zu zählen. Zählen der Zeilen in /usr/share/dict/words ergibt:

$ grep -c '.' /usr/share/dict/words 
479826

Der '.' bedeutet, dass alle Zeilen gezählt werden, die mindestens ein Zeichen, Leerzeichen, Leerzeichen, Tabulatoren usw. enthalten.

Grundlegendes grep reguläre Ausdrücke

Das grep Der Befehl wird leistungsfähiger, wenn wir reguläre Ausdrücke (regexes) verwenden. Während wir uns also auf grep konzentrieren Befehl selbst, werden wir auch auf die grundlegende Syntax regulärer Ausdrücke eingehen.

Nehmen wir an, wir interessieren uns nur für Wörter, die mit Z beginnen . In dieser Situation sind Regexes praktisch. Wir verwenden das Karat (^ ), um nach Mustern zu suchen, die mit einem bestimmten Zeichen beginnen, das den Anfang einer Zeichenfolge bezeichnet:

Um nach Mustern zu suchen, die mit einem bestimmten Zeichen enden, verwenden wir das Dollarzeichen ($ ) um das Ende der Zeichenfolge zu kennzeichnen. Sehen Sie sich das Beispiel unten an, wo wir nach Zeichenfolgen suchen, die mit hat enden :

Um alle Zeilen zu drucken, die hat enthalten Unabhängig von seiner Position, ob am Zeilenanfang oder am Zeilenende, würden wir so etwas verwenden wie:

Der ^ und $ werden als Metazeichen bezeichnet und sollten mit einem Backslash (\ ), wenn wir diese Zeichen buchstäblich abgleichen möchten. Wenn Sie mehr über Metazeichen erfahren möchten, lesen Sie https://www.regular-expressions.info/characters.html.

Beispiel:Kommentare entfernen

Jetzt, wo wir an der Oberfläche von grep gekratzt haben , lassen Sie uns an einigen realen Szenarien arbeiten. Viele Konfigurationsdateien in *nix enthalten Kommentare, die verschiedene Einstellungen innerhalb der Konfigurationsdatei beschreiben. Die /etc/fstab , Datei hat zum Beispiel:

$ cat /etc/fstab 

#
# /etc/fstab
# Created by anaconda on Thu Oct 27 05:06:06 2016
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/VGCRYPTO-ROOT /                       ext4    defaults,x-systemd.device-timeout=0 1 1
UUID=e9de0f73-ddddd-4d45-a9ba-1ffffa /boot                   ext4    defaults        1 2
LABEL=SSD_SWAP		swap		swap		defaults	0 0
#/dev/mapper/VGCRYPTO-SWAP swap                    swap    defaults,x-systemd.device-timeout=0 0 0

Die Kommentare sind durch die Raute gekennzeichnet (# ), und wir möchten sie ignorieren, wenn sie gedruckt werden. Eine Option ist die cat Befehl:

$ cat /etc/fstab | grep -v '^#' 

Sie brauchen jedoch cat nicht hier (vermeiden Sie die nutzlose Verwendung von Cat). Das grep Der Befehl ist perfekt in der Lage, Dateien zu lesen, also können Sie stattdessen etwas wie das Folgende verwenden, um Zeilen zu ignorieren, die Kommentare enthalten:

$ grep -v '^#' /etc/fstab 

Wenn Sie die Ausgabe (ohne Kommentare) stattdessen an eine andere Datei senden möchten, verwenden Sie:

$ grep -v '^#' /etc/fstab > ~/fstab_without_comment

Während grep die Ausgabe auf dem Bildschirm formatieren kann, ist dieser Befehl nicht in der Lage, eine Datei an Ort und Stelle zu ändern. Dazu benötigen wir einen Dateieditor wie ed . Im nächsten Artikel verwenden wir sed um dasselbe zu erreichen, was wir hier mit grep gemacht haben .

Beispiel:Kommentare und Leerzeilen entfernen

Wo wir noch bei grep sind , sehen wir uns die Datei /etc/sudoers an Datei. Diese Datei enthält viele Kommentare, aber wir interessieren uns nur für Zeilen, die keine Kommentare haben, und wir wollen auch die leeren Zeilen loswerden.

Entfernen wir also zuerst die Zeilen mit den Kommentaren. Die folgende Ausgabe wird erzeugt:

# grep -v '^#' /etc/sudoers 

Defaults   !visiblepw

Defaults    env_reset
Defaults    env_keep =  "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
Defaults    env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults    env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults    env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults    env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"
Defaults    secure_path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
root	ALL=(ALL) 	ALL
%wheel	ALL=(ALL)	ALL

Jetzt wollen wir die leeren (leeren) Zeilen loswerden. Nun, das ist einfach, führen Sie einfach ein weiteres grep aus Befehl:

# grep -v '^#' /etc/sudoers | grep -v '^$' 
Defaults   !visiblepw
Defaults    env_reset
Defaults    env_keep =  "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
Defaults    env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults    env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults    env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults    env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"
Defaults    secure_path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
root	ALL=(ALL) 	ALL
%wheel	ALL=(ALL)	ALL
valentin.local	ALL=NOPASSWD: /usr/bin/updatedb

Könnten wir es besser machen? Können wir unser grep ausführen Befehl ressourcenfreundlicher zu sein und grep nicht zu verzweigen zweimal? Das können wir auf jeden Fall:

# grep -Ev '^#|^$' /etc/sudoers 
Defaults   !visiblepw
Defaults    env_reset
Defaults    env_keep =  "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
Defaults    env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults    env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults    env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults    env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"
Defaults    secure_path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
root	ALL=(ALL) 	ALL
%wheel	ALL=(ALL)	ALL
valentin.local	ALL=NOPASSWD: /usr/bin/updatedb

Hier haben wir ein weiteres grep eingeführt Option -E (oder --extended-regexp ) <PATTERN> ist ein erweiterter regulärer Ausdruck.

Beispiel:Nur /etc/passwd drucken Benutzer

Es ist offensichtlich, dass grep ist leistungsfähig, wenn es mit regulären Ausdrücken verwendet wird. Dieser Artikel behandelt nur einen kleinen Teil von grep ist wirklich in der Lage. Um die Fähigkeiten von grep zu demonstrieren und die Verwendung von regulären Ausdrücken analysieren wir die Datei /etc/passwd Datei und drucke nur die Benutzernamen.

Das Format von /etc/passwd Datei ist wie folgt:

$ head /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin

Die obigen Felder haben folgende Bedeutung:

<name>:<password>:<UID>:<GID>:<GECOS>:<directory>:<shell>

Siehe man 5 passwd Weitere Informationen finden Sie unter /etc/passwd Datei. Um nur die Benutzernamen auszugeben, könnten wir etwa Folgendes verwenden:

$ grep -Eo '^[a-zA-Z_-]+' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
operator

Im obigen grep Befehl haben wir eine weitere Option eingeführt:-o (oder --only-matching ), um nur den Teil einer Zeile anzuzeigen, der <PATTERN> entspricht . Dann haben wir -Eo kombiniert um das gewünschte Ergebnis zu erhalten.

Wir werden den obigen Befehl jetzt auflösen, damit wir besser verstehen können, was wirklich passiert. Von links nach rechts:

  • ^ Übereinstimmungen am Anfang der Zeile.
  • [a-zA-Z_-] wird als Zeichenklasse bezeichnet und passt zu einer Liste, die mit einem einzelnen Zeichen übereinstimmt.
  • + ist ein Quantifizierer, der zwischen einer und einer unbegrenzten Anzahl von Malen übereinstimmt.

Der obige reguläre Ausdruck wiederholt sich, bis er ein Zeichen erreicht, mit dem er nicht übereinstimmt. Die erste Zeile der Datei lautet:

root:x:0:0:root:/root:/bin/bash

Es wird wie folgt verarbeitet:

  1. Das erste Zeichen ist ein r , also wird es mit [a-z] abgeglichen .
  2. Der + springt zum nächsten Zeichen.
  3. Das zweite Zeichen ist ein o und dies wird durch [a-z] abgeglichen .
  4. Der + springt zum nächsten Zeichen.

Diese Sequenz wiederholt sich, bis wir den Doppelpunkt treffen (: ). Die Zeichenklasse [a-zA-Z_-] stimmt nicht mit : überein Symbol, also grep wechselt zur nächsten Zeile.

Da die Benutzernamen im passwd Datei alle Kleinbuchstaben sind, könnten wir unsere Zeichenklasse auch wie folgt vereinfachen und trotzdem das gewünschte Ergebnis erhalten:

$ grep -Eo '^[a-z_-]+' /etc/passwd

Beispiel:Einen Prozess finden

Bei Verwendung von ps Um nach einem Prozess zu suchen, verwenden wir oft etwas wie:

$ ps aux | grep ‘thunderbird’

Aber das ps Der Befehl listet nicht nur thunderbird auf Prozess. Es listet auch grep auf Befehl, den wir gerade ausgeführt haben, seit grep läuft auch nach der Pipe und wird in der Prozessliste angezeigt:

$ ps aux | grep thunderbird
val+  2196  0.7  2.1 52 33 tty2  Sl+ 16:47  1:55 /usr/lib64/thunderbird/thunderbird
val+ 14064  0.0  0.0 57 82 pts/2  S+   21:12   0:00 grep --color=auto thunderbird

Wir können dies handhaben, indem wir grep -v grep hinzufügen um grep auszuschließen aus der Ausgabe:

$ ps aux | grep thunderbird | grep -v grep
val+  2196  0.7  2.1 52 33 tty2  Sl+ 16:47  1:55 /usr/lib64/thunderbird/thunderbird

Bei Verwendung von grep -v grep tun, was wir wollten, es gibt bessere Möglichkeiten, dasselbe Ergebnis zu erzielen, ohne ein neues grep zu forken Prozess:

$ ps aux | grep [t]hunderbird 
val+  2196  0.7  2.1 52 33 tty2  Sl+ 16:47  1:55 /usr/lib64/thunderbird/thunderbird

Der [t]hunderbird entspricht hier dem wörtlichen t , und unterscheidet zwischen Groß- und Kleinschreibung. Es passt nicht zu grep , und deshalb sehen wir jetzt nur noch thunderbird in der Ausgabe.

Dieses Beispiel ist nur eine Demonstration, wie flexibel grep ist ist, hilft Ihnen nicht bei der Fehlersuche in Ihrem Prozessbaum. Dafür gibt es bessere Tools wie pgrep .

Zusammenfassung

Verwenden Sie grep wenn Sie nach einem Muster suchen möchten, entweder in einer Datei oder mehreren Verzeichnissen rekursiv. Versuchen Sie zu verstehen, wie reguläre Ausdrücke bei grep funktionieren , da reguläre Ausdrücke mächtig sein können.

[Möchten Sie Red Hat Enterprise Linux ausprobieren? Jetzt kostenlos herunterladen.]


Linux
  1. Lassen Sie sich mit sort in der Befehlszeile sortieren

  2. Arbeiten mit Datenströmen auf der Linux-Befehlszeile

  3. Arbeiten mit Pipes auf der Linux-Befehlszeile

  4. Bearbeiten von Text auf der Kommandozeile mit sed

  5. Multitasking auf der Kommandozeile mit Screenie

So verwenden Sie Nano, den Linux-Befehlszeilen-Texteditor

Verwenden des GREP-Befehls unter Linux mit Beispielen

Meistern Sie die Linux-Befehlszeile

Das Grep-Befehls-Tutorial mit Beispielen für Anfänger

Bearbeiten von Text mit sed und grep

Zeiterfassung mit Timewarrior auf der Kommandozeile