13 Shell-basierte Textverarbeitungswerkzeuge

Hier ist ein Fragment eines zukünftigen Buches, Grundlegende Tools und Praktiken für einen unerfahrenen Softwareentwickler, von Balthazar Ruberol und Etienne Broad . Das Buch soll dazu beitragen, die jüngere Generation von Entwicklern zu erziehen. Es werden Themen wie die Konsole zu meistern, Einrichten und effizient in der Kommando - Shell arbeiten, Versionierung Code mit gitSQL - Grundlagen, Werkzeuge wie Make, jqund reguläre Ausdrücke, Grundlagen der Vernetzung sowie Best Practices für die Software - Entwicklung und Zusammenarbeit. Die Autoren arbeiten derzeit intensiv an diesem Projekt und laden alle ein, an der Mailingliste teilzunehmen .

Inhalt



Shell-Textverarbeitung


Einer der Gründe, die die Befehlsshell zu einem unschätzbaren Werkzeug machen, ist die große Anzahl von Textverarbeitungsbefehlen und die Möglichkeit, sie einfach in der Pipeline zu kombinieren und komplexe Verarbeitungsvorlagen zu erstellen. Diese Befehle machen viele Aufgaben zum Analysieren von Text und Daten, Konvertieren von Daten zwischen verschiedenen Formaten, Filtern von Zeichenfolgen usw. trivial.

Bei der Arbeit mit Textdaten besteht das Hauptprinzip darin, jedes komplexe Problem in viele kleinere zu unterteilen - und jedes zu lösen von ihnen mit einem speziellen Werkzeug.

Lassen Sie jedes Programm eines gut machen - Die Grundlagen der Unix-Philosophie

Die Beispiele in diesem Kapitel mögen auf den ersten Blick etwas weit hergeholt erscheinen, aber dies geschieht absichtlich. Jedes der Tools wurde entwickelt, um ein kleines Problem zu lösen. In Kombination werden sie jedoch extrem leistungsfähig.

Wir werden uns einige der häufigsten und nützlichsten Textverarbeitungsbefehle in der Befehlsshell ansehen und echte Workflows demonstrieren, die sie miteinander verbinden. Ich schlage vor, das Mana dieser Teams zu betrachten, um die volle Bandbreite der Möglichkeiten zu sehen, die Ihnen zur Verfügung stehen.

Eine Beispiel-CSV-Datei ist online verfügbar . Sie können es herunterladen, um das Material zu überprüfen.

Katze


Mit dem Befehl wird cateine Liste mit einer oder mehreren Dateien erstellt und deren Inhalt auf dem Bildschirm angezeigt.

$ cat Documents/readme
Thanks again for reading this book!
I hope you're following so far!

$ cat Documents/computers
Computers are not intelligent
They're just fast at making dumb things.

$ cat Documents/readme Documents/computers
Thanks again for reading this book!
I hope you are following so far!

Computers are not intelligent
They're just fast at making dumb things.

Kopf


headdruckt die ersten n Zeilen in der Datei. Dies kann sehr nützlich sein, um eine Datei mit unbekannter Struktur und unbekanntem Format zu untersuchen, ohne die gesamte Konsole mit einer Menge Text zu füllen.

$ head -n 2 metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name
mysql.galera.wsrep_cluster_size,gauge,,node,,The current number of nodes in the Galera cluster.,0,mysql,galera cluster size

Wenn -nnicht angegeben, werden headdie ersten zehn Zeilen der angegebenen Datei oder des angegebenen Eingabestreams gedruckt.

Schwanz


tail- ein Analogon head, nur die letzten n Zeilen in der Datei werden angezeigt.

$ tail -n 1 metadata.csv
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries

Wenn Sie alle Zeilen drucken möchten, die sich nach der n-ten Zeile befinden (einschließlich dieser), können Sie das Argument verwenden -n +n.

$ tail -n +42 metadata.csv
mysql.replication.slaves_connected,gauge,,,,Number of slaves connected to a replication master.,0,mysql,slaves connected
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries

Unsere Datei enthält 43 Zeilen, daher werden tail -n +42nur die 42. und 43. Zeile ausgegeben.

Wenn der Parameter -nnicht angegeben wird, tailwerden die letzten zehn Zeilen in der angegebenen Datei oder im angegebenen Eingabestream ausgegeben.

tail -foder tail --followzeigen Sie die letzten Zeilen in der Datei und jede neue Zeile an, während sie in die Datei geschrieben werden. Dies ist sehr nützlich, um Aktivitäten in Echtzeit anzuzeigen, z. B. was in den Webserver-Protokollen aufgezeichnet ist usw.

Toilette


wc(Wortanzahl) zeigt die Anzahl der Zeichen ( -c), Wörter ( -w) oder Zeilen ( -l) in der angegebenen Datei oder dem angegebenen Stream an.

$ wc -l metadata.csv
43  metadata.csv
$ wc -w metadata.csv
405 metadata.csv
$ wc -c metadata.csv
5094 metadata.csv

Standardmäßig werden alle oben genannten Elemente angezeigt.

$ wc metadata.csv
43     405    5094 metadata.csv

Wenn Textdaten weitergeleitet oder umgeleitet werden, stdinwird nur der Zähler angezeigt.

$ cat metadata.csv | wc
43     405    5094
$ cat metadata.csv | wc -l
43
$ wc -w < metadata.csv
405

grep


grep- Dies ist ein Schweizer Messer, das Saiten nach einem bestimmten Muster filtert.

Zum Beispiel können wir alle Vorkommen des Wortes Mutex in einer Datei finden.

$ grep mutex metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.mutex_spin_rounds,gauge,,event,second,The rate of mutex spin rounds.,0,mysql,mutex spin rounds
mysql.innodb.mutex_spin_waits,gauge,,event,second,The rate of mutex spin waits.,0,mysql,mutex spin waits

grepkann entweder als Argumente angegebene Dateien oder einen an sie übergebenen Textstrom verarbeiten stdin. Somit können wir mehrere Befehle verketten grep, um den Text weiter zu filtern. Im folgenden Beispiel filtern wir die Zeilen in unserer Datei metadata.csv, um Zeilen zu finden, die sowohl Mutex als auch Betriebssystem enthalten .

$ grep mutex metadata.csv | grep OS
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits

Betrachten wir einige Optionen grepund ihr Verhalten.

grep -vFührt einen inversen Abgleich durch: Filtert Zeichenfolgen, die nicht mit dem Argumentmuster übereinstimmen.

$ grep -v gauge metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name

grep -iFührt einen Matching ohne Berücksichtigung der Groß- und Kleinschreibung durch. Das folgende Beispiel grep -i osfindet sowohl OS und OS .

$ grep -i os metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.os_log_fsyncs,gauge,,write,second,The rate of fsync writes to the log file.,0,mysql,log fsyncs

grep -l Listet Dateien auf, die eine Übereinstimmung enthalten.

$ grep -l mysql metadata.csv
metadata.csv

Das Team grep -czählt, wie oft eine Probe gefunden wurde.

$ grep -c select metadata.csv
3

grep -r Sucht rekursiv nach Dateien im aktuellen Arbeitsverzeichnis und allen seinen Unterverzeichnissen.

$ grep -r are ~/Documents
/home/br/Documents/computers:Computers are not intelligent
/home/br/Documents/readme:I hope you are following so far!

grep -w zeigt nur übereinstimmende ganze Wörter.

$ grep follow ~/Documents/readme
I hope you are following so far!
$ grep -w follow ~/Documents/readme
$

Schnitt


cutextrahiert einen Teil der Datei (oder wie üblich den Eingabestream). Der Befehl definiert das Feldtrennzeichen (das die Spalten trennt) mithilfe der Option -dund die Spaltennummern, die mithilfe der Option abgerufen werden sollen -f.

Mit dem folgenden Befehl wird beispielsweise die erste Spalte aus den letzten fünf Zeilen unserer CSV-Datei abgerufen.

$ tail -n 5 metadata.csv | cut -d , -f 1
mysql.performance.user_time
mysql.replication.seconds_behind_master
mysql.replication.slave_running
mysql.replication.slaves_connected
mysql.performance.queries

Da es sich um CSV handelt, werden die Spalten durch ein Komma getrennt, und die Option ist für das Abrufen der ersten Spalte verantwortlich -f 1.

Mit der Option können Sie sowohl die erste als auch die zweite Spalte auswählen -f 1,2.

$ tail -n 5 metadata.csv | cut -d , -f 1,2
mysql.performance.user_time,gauge
mysql.replication.seconds_behind_master,gauge
mysql.replication.slave_running,gauge
mysql.replication.slaves_connected,gauge
mysql.performance.queries,gauge

Einfügen


paste führt zwei verschiedene Dateien zu einer mehrspaltigen Datei zusammen.

$ cat ingredients
eggs
milk
butter
tomatoes
$ cat prices
1$
1.99$
1.50$
2$/kg
$ paste ingredients prices
eggs    1$
milk    1.99$
butter  1.50$
tomatoes    2$/kg

Standardmäßig pastewird ein Tabulatortrennzeichen verwendet, das jedoch über den Parameter geändert werden kann -d.

$ paste ingredients prices -d:
eggs:1$
milk:1.99$
butter:1.50$
tomatoes:2$/kg

Ein weiterer häufiger Anwendungsfall paste besteht darin, alle Zeilen in einem Stream oder einer Datei mit dem angegebenen Trennzeichen und einer Kombination aus -sund zu kombinieren -d.

$ paste -s -d, ingredients
eggs,milk,butter,tomatoes

Wenn ein Parameter als Eingabedatei angegeben -wird, wird er stattdessen gelesen stdin.

$ cat ingredients | paste -s -d, -
eggs,milk,butter,tomatoes

Sortieren


Der Befehl sortsortiert die Daten tatsächlich (in der angegebenen Datei oder im angegebenen Eingabestream).

$ cat ingredients
eggs
milk
butter
tomatoes
salt
$ sort ingredients
butter
eggs
milk
salt
tomatoes

sort -r führt eine umgekehrte Sortierung durch.

$ sort -r ingredients
tomatoes
salt
milk
eggs
butter

sort -n Sortiert Felder nach ihrem arithmetischen Wert.

$ cat numbers
0
2
1
10
3
$ sort numbers
0
1
10
2
3
$ sort -n numbers
0
1
2
3
10

uniq


uniq Erkennt und filtert benachbarte identische Zeilen in der angegebenen Datei oder im angegebenen Eingabestream.

$ cat duplicates
and one
and one
and two
and one
and two
and one, two, three
$ uniq duplicates
and one
and two
and one
and two
and one, two, three

Da uniqnur benachbarte Zeilen herausgefiltert werden, verbleiben möglicherweise noch Duplikate in unseren Daten. Um dieselben Zeilen aus einer Datei zu filtern, müssen Sie zuerst deren Inhalt sortieren.

$ sort duplicates | uniq
and one
and one, two, three
and two

uniq -c Am Anfang jeder Zeile wird die Anzahl ihrer Vorkommen eingefügt.

$ sort duplicates | uniq -c
   3 and one
   1 and one, two, three
   2 and two

uniq -u Zeigt nur eindeutige Zeichenfolgen an.

$ sort duplicates | uniq -u
and one, two, three

Hinweis. uniqDies ist besonders in Kombination mit dem Sortieren nützlich, da Sie mit der Pipeline | sort | uniqalle doppelten Zeilen in einer Datei oder einem Stream löschen können.

awk


awk- Dies ist etwas mehr als nur ein Textverarbeitungswerkzeug: Tatsächlich verfügt es über eine ganze Programmiersprache . Was awk wirklich gut ist, ist das Aufteilen von Dateien in Spalten, und dies mit besonderer Brillanz, wenn Leerzeichen und Tabulatoren in Dateien gemischt werden.

$ cat -t multi-columns
John Smith    Doctor^ITardis
Sarah-James Smith^I    Companion^ILondon
Rose Tyler   Companion^ILondon

Hinweis. cat -tzeigt Registerkarten als an ^I.

Wie Sie sehen können, sind die Spalten entweder durch Leerzeichen oder Tabulatoren getrennt und nicht immer durch die gleiche Anzahl von Leerzeichen. cutes ist hier nutzlos, weil es nur mit einem Trennzeichen funktioniert. Es ist awkjedoch einfach, mit einer solchen Datei umzugehen.

awk '{ print $n }'Zeigt die n-te Spalte im Text an.

$ cat multi-columns | awk '{ print $1 }'
John
Sarah-James
Rose
$ cat multi-columns | awk '{ print $3 }'
Doctor
Companion
Companion
$ cat multi-columns | awk '{ print $1,$2 }'
John Smith
Sarah-James Smith
Rose Tyler

Obwohl awkzu viel mehr fähig, ist die Ausgabe der Lautsprecher wahrscheinlich 99% der Anwendungsfälle in meinem persönlichen Fall.

Hinweis. { print $NF }Zeigt die letzte Spalte in einer Zeile an.

tr


trsteht für übersetzen . Dieser Befehl ersetzt ein Zeichen durch ein anderes. Es arbeitet entweder mit Zeichen oder Zeichenklassen wie Kleinbuchstaben eingegeben, Leerzeichen, alphanumerische, usw.

Auf der Standardeingabe, es tr <char1> <char2>ersetzt alle Vorkommen von <char1> mit <char2>.

$ echo "Computers are fast" | tr a A
computers Are fAst

trkann Zeichenklassen mit Notation übersetzen [:class:]. Eine vollständige Liste der verfügbaren Klassen ist auf der Manpage beschrieben tr, einige werden hier gezeigt.

[:space:]repräsentiert alle Arten von Leerzeichen, von einfachen Leerzeichen bis zu Tabulatoren oder Zeilenumbrüchen.

$ echo "computers are fast" | tr '[:space:]' ','
computers,are,fast,%

Alle Zeichen sind wie Leerzeichen durch Kommas getrennt. Bitte beachten Sie, dass das Zeichen %am Ende der Ausgabe das Fehlen einer abschließenden neuen Zeile anzeigt. In der Tat wird dieses Zeichen auch in ein Komma umgewandelt.

[:lower:]repräsentiert alle Kleinbuchstaben und [:upper:] alle Großbuchstaben. Somit wird die Transformation zwischen ihnen trivial.

$ echo "computers are fast" | tr '[:lower:]' '[:upper:]'
COMPUTERS ARE FAST
$ echo "COMPUTERS ARE FAST" | tr '[:upper:]' '[:lower:]'
computers are fast

tr -c SET1 SET2konvertiert alle Zeichen, die nicht in SET1 enthalten sind, in Zeichen in SET2. Im folgenden Beispiel werden alle Zeichen außer den angegebenen Vokalen durch Leerzeichen ersetzt.

$ echo "computers are fast" | tr -c '[aeiouy]' ' '
 o  u e   a e  a

tr -dLöscht die angegebenen Zeichen, ersetzt sie jedoch nicht. Dies ist das Äquivalent tr <char> ''.

$ echo "Computers Are Fast" | tr -d '[:lower:]'
C A F

trkann auch Zeichenbereiche ersetzen, z. B. alle Buchstaben zwischen a und e oder alle Zahlen zwischen 1 und 8 unter Verwendung der Notation s-e, wobei s das Startzeichen und e das Ende ist.

$ echo "computers are fast" | tr 'a-e' 'x'
xomputxrs xrx fxst
$ echo "5uch l337 5p34k" | tr '1-4' 'x'
5uch lxx7 5pxxk

Der Befehl tr -s string1komprimiert alle mehrfachen Vorkommen von Zeichen string1in einem einzigen. Eine der nützlichsten Anwendungen tr -sbesteht darin, mehrere aufeinanderfolgende Leerzeichen durch ein Leerzeichen zu ersetzen.

$ echo "Computers         are       fast" | tr -s ' '
Computers are fast

falten


Der Befehl foldreduziert alle Eingabezeilen auf die angegebene Breite. Beispielsweise kann es hilfreich sein, sicherzustellen, dass der Text auf kleine Displays passt. So fold -w nstapelt Saiten Breite n Zeichen.

$ cat ~/Documents/readme | fold -w 16
Thanks again for
 reading this bo
ok!
I hope you're fo
llowing so far!

Der Befehl fold -sunterbricht Zeilen nur bei Leerzeichen. Es kann mit dem vorherigen kombiniert werden, um die Zeichenfolge auf die angegebene Anzahl von Zeichen zu beschränken.

Thanks again
for reading
this book!
I hope you're
following so
far!

sed


sedIst ein nicht interaktiver Stream-Editor, mit dem Text im Eingabestream zeilenweise konvertiert wird. Als Eingabe entweder eine Datei oder oder stdinbei der Ausgabe entweder eine Datei oder stdout.

Editorbefehle können eine oder mehrere Adressen , eine Funktion und Parameter enthalten . Somit sind die Befehle wie folgt:

[address[,address]]function[arguments]

Obwohl es sedviele Funktionen ausführt, werden wir nur das Ersetzen von Text als einen der häufigsten Anwendungsfälle betrachten.

Textersetzung


Der Ersetzungsbefehl lautet sedwie folgt:

s/PATTERN/REPLACEMENT/[options]

Beispiel : Ersetzen der ersten Instanz eines Wortes in jeder Zeile einer Datei:

$ cat hello
hello hello
hello world!
hi
$ cat hello | sed 's/hello/Hey I just met you/'
Hey I just met you hello
Hey I just met you world
hi

Wir sehen, dass in der ersten Zeile nur die erste Instanz ersetzt wird hello. Um alle Vorkommen helloin allen Zeilen zu ersetzen , können Sie die Option g(bedeutet global ) verwenden.

$ cat hello | sed 's/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi

sedErmöglicht die Verwendung von Trennzeichen außer /, was insbesondere die Lesbarkeit verbessert, wenn die Befehlsargumente selbst Schrägstriche enthalten.

$ cat hello | sed 's@hello@Hey I just met you@g'
Hey I just met you Hey I just met you
Hey I just met you world
hi

Die Adresse teilt dem Editor mit, in welcher Zeile oder in welchem ​​Zeilenbereich die Ersetzung durchgeführt werden soll.

$ cat hello | sed '1s/hello/Hey I just met you/g'
Hey I just met you hello
hello world
hi
$ cat hello | sed '2s/hello/Hey I just met you/g'
hello hello
Hey I just met you  world
hi

Die Adresse 1gibt in der ersten Zeile Ersetzen helloan Hey I just met you. Wir können den Adressbereich in der Notation angeben <start>,<end>, wobei es <end>sich entweder um die Zeilennummer oder $die letzte Zeile in der Datei handeln kann.

$ cat hello | sed '1,2s/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
$ cat hello | sed '2,3s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
$ cat hello | sed '2,$s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi

Standardmäßig sedwird das Ergebnis selbst erstellt stdout, es kann jedoch auch die Originaldatei mit der Option bearbeitet werden -i.

$ sed -i '' 's/hello/Bonjour/' sed-data
$ cat sed-data
Bonjour hello
Bonjour world
hi

Hinweis. Unter Linux gerade genug -i. Unter macOS unterscheidet sich das Verhalten des Befehls jedoch geringfügig, sodass -iSie ihn direkt danach hinzufügen müssen ''.

Echte Beispiele


CSV-Filterung mit grep und awk


$ grep -w gauge metadata.csv | awk -F, '{ if ($4 == "query") { print $1, "per", $5 } }'
mysql.performance.com_delete per second
mysql.performance.com_delete_multi per second
mysql.performance.com_insert per second
mysql.performance.com_insert_select per second
mysql.performance.com_replace_select per second
mysql.performance.com_select per second
mysql.performance.com_update per second
mysql.performance.com_update_multi per second
mysql.performance.questions per second
mysql.performance.slow_queries per second
mysql.performance.queries per second

In diesem Beispiel filtert grepdie Datei metadata.csvzuerst die Zeilen, die das Wort enthalten gauge, dann die Zeilen mit der queryvierten Spalte und zeigt den Metriknamen (1. Spalte) mit dem entsprechenden Wert per_unit_name(5. Spalte) an.

Zeigt die IPv4-Adresse an, die der Netzwerkschnittstelle zugeordnet ist


$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38

Der Befehl ifconfig <interface name>zeigt Informationen zur angegebenen Netzwerkschnittstelle an. Zum Beispiel:

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ether 19:64:92:de:20:ba
    inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
    inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active

Dann laufen Sie grepfür inet, das ergibt zwei Korrespondenzzeilen.

$ ifconfig en0 | grep inet
    inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
    inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255

Verwenden Sie dann grep -vAusschließen der Zeile mit ipv6.

$ ifconfig en0 | grep inet | grep -v inet6
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255

Schließlich awkfordern wir mit Hilfe die zweite Spalte in dieser Zeile an: Dies ist die IPv4-Adresse, die unserer Netzwerkschnittstelle zugeordnet ist en0.

$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38

Hinweis. Mir wurde angeboten , durch grep inet | grep -v inet6ein so zuverlässiges Team zu ersetzen awk:

$ ifconfig en0 | awk ' $1 == "inet" { print $2 }'
192.168.0.38

Es ist kürzer und speziell auf IPv4 mit der Bedingung ausgerichtet $1 == "inet".

Abrufen eines Werts aus einer Konfigurationsdatei


$ grep 'editor =' ~/.gitconfig  | cut -d = -f2 | sed 's/ //g'
/usr/bin/vim

Suchen Sie in der Git-Konfigurationsdatei des aktuellen Benutzers nach dem Wert editor =, schneiden Sie das Zeichen zu =, extrahieren Sie die zweite Spalte und löschen Sie alle Leerzeichen.

$ grep 'editor =' ~/.gitconfig
     editor = /usr/bin/vim
$ grep 'editor =' ~/.gitconfig  | cut -d'=' -f2
 /usr/bin/vim
$ grep 'editor =' ~/.gitconfig  | cut -d'=' -f2 | sed 's/ //'
/usr/bin/vim

Extrahieren Sie IPs aus einer Protokolldatei


Der folgende echte Code sucht im Datenbankprotokoll nach einer Nachricht Too many connections from(gefolgt von einer IP-Adresse) und zeigt die zehn wichtigsten Eindringlinge an.

$ grep 'Too many connections from' db.log | \
  awk '{ print $12 }' | \
  sed 's@/@@' | \
  sort | \
  uniq -c | \
  sort -rn | \
  head -n 10 | \
  awk '{ print $2 }'
   10.11.112.108
   10.11.111.70
   10.11.97.57
   10.11.109.72
   10.11.116.156
   10.11.100.221
   10.11.96.242
   10.11.81.68
   10.11.99.112
   10.11.107.120

Mal sehen, was diese Pipeline macht. Wie sieht die Zeile im Protokoll aus?

$ grep "Too many connections from" db.log | head -n 1
2020-01-01 08:02:37,617 [myid:1] - WARN  [NIOServerCxn.Factory:1.2.3.4/1.2.3.4:2181:NIOServerCnxnFactory@193] - Too many connections from /10.11.112.108 - max is 60

Anschließend wird awk '{ print $12 }'die IP-Adresse aus der Zeichenfolge extrahiert.

$ grep "Too many connections from" db.log | awk '{ print $12 }'
/10.11.112.108
...

Der Befehl sed 's@/@@'löscht den anfänglichen Schrägstrich.

$ grep "Too many connections from" db.log | awk '{ print $12 }' | sed 's@/@@'
10.11.112.108
...

Hinweis. Wie wir bereits gesehen haben, können sedSie in jedem Trennzeichen verwenden. Obwohl es normalerweise als Trennzeichen verwendet wird /, ersetzen wir hier dieses spezielle Zeichen, was die Lesbarkeit des Substitutionsausdrucks geringfügig beeinträchtigt.

sed 's/\///'

sort | uniq -c sortiert IP-Adressen in lexikografischer Reihenfolge und entfernt dann Duplikate, wobei die Anzahl der Einträge vor jeder IP-Adresse hinzugefügt wird.

$ grep 'Too many connections from' db.log | \
  awk '{ print $12 }' | \
  sed 's@/@@' | \
  sort | \
  uniq -c
   1379 10.11.100.221
   1213 10.11.103.168
   1138 10.11.105.177
    946 10.11.106.213
   1211 10.11.106.4
   1326 10.11.107.120
   ...

sort -rn | head -n 10sortiert die Zeilen nach der Anzahl der Vorkommen, numerisch und in umgekehrter Reihenfolge, sodass zuerst die Hauptverletzer angezeigt werden, von denen 10 Zeilen angezeigt werden. Der letzte Befehl awk { print $2 }ruft die IP-Adressen selbst ab.

$ grep 'Too many connections from' db.log | \
  awk '{ print $12 }' | \
  sed 's@/@@' | \
  sort | \
  uniq -c | \
  sort -rn | \
  head -n 10 | \
  awk '{ print $2 }'
  10.11.112.108
  10.11.111.70
  10.11.97.57
  10.11.109.72
  10.11.116.156
  10.11.100.221
  10.11.96.242
  10.11.81.68
  10.11.99.112
  10.11.107.120

Umbenennen einer Funktion in der Quelldatei


Stellen Sie sich vor, wir arbeiten an einem Projekt und möchten die unterbenannte Funktion (oder Klasse, Variable usw.) in der Quelldatei umbenennen. Sie können dies mit einem Befehl tun sed -i, der direkt in der Originaldatei ersetzt wird.

$ cat izk/utils.py
def bool_from_str(s):
    if s.isdigit():
        return int(s) == 1
    return s.lower() in ['yes', 'true', 'y']

$ sed -i 's/def bool_from_str/def is_affirmative/' izk/utils.py
$ cat izk/utils.py
def is_affirmative(s):
    if s.isdigit():
        return int(s) == 1
    return s.lower() in ['yes', 'true', 'y']

Hinweis. sed -iVerwenden Sie stattdessen unter macOS sed -i ''.

Wir haben die Funktion jedoch nur in der Originaldatei umbenannt. Dadurch wird der Import bool_from_strin eine andere Datei unterbrochen, da diese Funktion nicht mehr definiert ist. Wir müssen einen Weg finden, um bool_from_strwährend unseres Projekts umzubenennen . Dies kann erreicht werden unter Verwendung von Befehlen grep, sedsowie Schleifen foroder Verwendung xargs.

Vertiefung: Zyklen forundxargs


Um alle Vorkommen in unserem Projekt zu ersetzen bool_from_str, müssen Sie sie zuerst rekursiv mit finden grep -r.

$ grep -r bool_from_str .
./tests/test_utils.py:from izk.utils import bool_from_str
./tests/test_utils.py:def test_bool_from_str(s, expected):
./tests/test_utils.py:    assert bool_from_str(s) == expected
./izk/utils.py:def bool_from_str(s):
./izk/prompt.py:from .utils import bool_from_str
./izk/prompt.py:                    default = bool_from_str(os.environ[envvar])

Da wir nur an Dateien mit Übereinstimmungen interessiert sind, müssen Sie auch die Option verwenden -l/--files-with-matches:

-l, --files-with-matches
        Only the names of files containing selected lines are written to standard out-
        put.  grep will only search a file until a match has been found, making
        searches potentially less expensive.  Pathnames are listed once per file
        searched.  If the standard input is searched, the string ``(standard input)''
        is written.

$ grep -r --files-with-matches bool_from_str .
./tests/test_utils.py
./izk/utils.py
./izk/prompt.py

Dann können wir den Befehl verwenden xargs, um Aktionen aus jeder Zeile der Ausgabe auszuführen (dh aus allen Dateien, die die Zeile enthalten bool_from_str).

$ grep -r --files-with-matches bool_from_str . | \
  xargs -n 1 sed -i 's/bool_from_str/is_affirmative/'

Die Option -n 1gibt an, dass jede Zeile in der Ausgabe einen separaten Befehl ausführen soll sed.

Dann werden die folgenden Befehle ausgeführt:

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py

Wenn der Befehl, mit dem Sie xargs(in unserem Fall sed) aufrufen, mehrere Argumente unterstützt, sollten Sie das Argument -n 1für die Leistung verwerfen .

grep -r --files-with-matches bool_from_str . | xargs sed -i 's/bool_from_str/is_affirmative/'

Dieser Befehl wird dann ausgeführt

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py ./izk/utils.py ./izk/prompt.py

Hinweis. Aus der Zusammenfassung sedauf der Manpage geht hervor, dass das Team mehrere Argumente annehmen kann.

SYNOPSIS
     sed [-Ealn] command [file ...]
     sed [-Ealn] [-e command] [-f command_file] [-i extension] [file ...]

Wie wir im vorherigen Kapitel gesehen haben, file ...bedeutet dies, dass mehrere Argumente akzeptiert werden, bei denen es sich um Dateinamen handelt.

Wir sehen, dass für alle Vorkommen Ersetzungen vorgenommen werden bool_from_str.

$ grep -r is_affirmative .
./tests/test_utils.py:from izk.utils import is_affirmative
./tests/test_utils.py:def test_is_affirmative(s, expected):
./tests/test_utils.py:    assert is_affirmative(s) == expected
./izk/utils.py:def is_affirmative(s):
./izk/prompt.py:from .utils import is_affirmative
./izk/prompt.py:                    default = is_affirmative(os.environ[envvar])

Wie so oft gibt es verschiedene Möglichkeiten, um das gleiche Ergebnis zu erzielen. Stattdessen xargskönnten wir Schleifen verwenden for, um über die Zeilen in der Liste zu iterieren und für jedes Element Maßnahmen zu ergreifen. Diese Schleifen haben die folgende Syntax:

for item in list; do
    command $item
done

Wenn wir uns Befehl wickeln grepin $(), dann wird die Schale in einem ausführen Subshell , wobei das Ergebnis davon wird dann in einer Schleife wiederholt werden for.

$ for file in $(grep -r --files-with-matches bool_from_str .); do
  sed -i 's/bool_from_str/is_affirmative/' $file
done

Dieser Befehl wird ausgeführt

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py

Die Schleifensyntax forscheint mir klarer zu sein xargs, aber letztere kann Befehle parallel mithilfe von Parametern ausführen -P n, wobei ndie maximale Anzahl parallel ausgeführter Befehle gleichzeitig ausgeführt wird, was zu einem Leistungsgewinn führen kann.

Zusammenfassung


All diese Tools eröffnen eine ganze Welt von Möglichkeiten, da Sie damit Daten extrahieren und transformieren können und ganze Pipelines von Teams erstellen können, die möglicherweise nie zusammenarbeiten wollten. Jeder von ihnen führt eine relativ kleine Funktion aus (Sortieren sort, Kombinieren cat, Filtern grep, Bearbeiten sed, Schneiden cutusw.).

Jede Aufgabe, die Text enthält, kann auf eine Pipeline kleinerer Aufgaben reduziert werden, von denen jede eine einfache Aktion ausführt und ihre Ausgabe an die nächste Aufgabe überträgt.

Wenn wir beispielsweise wissen möchten, wie viele eindeutige IP-Adressen in der Protokolldatei enthalten sind und diese IP-Adressen immer in derselben Spalte angezeigt werden, können wir die folgende Befehlsfolge ausführen:

  • grep Zeichenfolgen, die dem Muster von Zeichenfolgen mit IP-Adressen entsprechen
  • Suchen Sie die Spalte mit der IP-Adresse und extrahieren Sie sie mit awk
  • Sortieren Sie die Liste der IP-Adressen mit sort
  • benachbarte Duplikate mit entfernen uniq
  • Zählen Sie die Anzahl der Zeilen (d. h. eindeutige IP-Adressen) mit wc -l

Da es viele native Textverarbeitungswerkzeuge von Drittanbietern gibt, gibt es auch viele Möglichkeiten, Probleme zu lösen.

Die Beispiele in diesem Artikel waren weit hergeholt, aber ich schlage vor, Sie lesen den erstaunlichen Artikel „Befehlszeilentools können 235-mal schneller sein als Ihr Hadoop-Cluster“ , um eine Vorstellung davon zu bekommen, wie nützlich und leistungsfähig diese Befehle wirklich sind und welche wirklichen Probleme auftreten sie können entscheiden.

Was weiter


  1. Zählen Sie die Anzahl der Dateien und Verzeichnisse in Ihrem Home-Verzeichnis.
  2. .
  3. , .
  4. . .



« » (Essential Tools and Practices for the Aspiring Software Developer) , . , , , , git, SQL, Make, jq , , .

, !

All Articles