Wie kann ich feldbasierte Daten über die Befehlszeile manipulieren? Zum Beispiel
- Wie kann ich nur Zeilen drucken, deren N-tes Feld
fooist ? - Wie kann ich nur Zeilen drucken, deren N-tes Feld nicht
fooist ? - Wie kann ich nur Zeilen drucken, deren N-tes Feld mit
fooübereinstimmt ? - Wie kann ich das Feld N in
fooändern ?
Gibt es einen Standardansatz oder ein Toolset, das die Bearbeitung feldbasierter Daten auf *nix-Systemen erleichtert?
Akzeptierte Antwort:
Es gibt zwei grundlegende Ansätze, die man beim Umgang mit Feldern verwenden kann:i) Verwenden Sie ein Werkzeug, das Felder versteht; ii) Verwenden Sie einen regulären Ausdruck. Von den beiden ist ersteres normalerweise sowohl robuster als auch einfacher.
Viele der allgemein verfügbaren Tools auf *nix sind entweder explizit darauf ausgelegt, mit Feldern umzugehen, oder haben raffinierte Tricks, um dies zu erleichtern.
1. Verwenden Sie ein Tool, das Felder versteht
1.1 awk
Das klassische Werkzeug hier ist awk . Es wird jede Eingabezeile automatisch in Felder aufteilen (das Feldtrennzeichen ist standardmäßig ein Leerzeichen, kann aber mit -F geändert werden flag) und die Felder stehen dann dem awk zur Verfügung Skript als $n wobei n ist die Feldnummer. Das erste Feld ist $1 , die zweite $2 usw.
-
Gibt Zeilen aus, deren drittes Feld
fooist .awk '$3=="foo"' fileÄnderung des Trennzeichens in
:awk -F":" '$3=="foo"' fileDie Standardaktion von
awkist zu drucken. Daher werden die obigen Befehle alle Zeilen drucken, deren drittes Feldfooist . Bei Verwendung von-F, Sie können beliebige Feldtrennzeichen setzen und sogar reguläre Ausdrücke verwenden. -
Wie kann ich nur Zeilen drucken, deren drittes Feld nicht
fooist ?awk '$3!="foo"' file -
Wie kann ich nur Zeilen drucken, deren drittes Feld
fooentspricht ?Wenn Sie nur nach Feldern suchen, die einem Muster entsprechen (zum Beispiel
fooentsprichtfoobar), verwenden Sie~statt==:awk '$3~/foo/' file -
Wie kann ich nur Zeilen drucken, deren drittes Feld nicht mit
fooübereinstimmt ?awk '$3!~/foo/' file -
Wie kann ich das 3. Feld in
fooändern ?awk '$3="foo"' file
1.2 Perl
Eine andere Wahl ist perl Einzeiler. Wie awk ist Perl eine Skriptsprache mit vollem Funktionsumfang, kann aber auch als Befehlszeilenprogramm ausgeführt werden, das ein Skript als Eingabe verwendet. Sein Verhalten wird durch Befehlszeilenschalter modifiziert, von denen die relevantesten für diese Frage sind:
-e:das Skript, dasperlsollte laufen;-n:liest die Eingabedatei Zeile für Zeile;-p:gibt jede Eingabezeile aus, nachdem das durch-eangegebene Skript angewendet wurde;-l:Entfernen Sie abschließende Zeilenumbrüche aus jeder Eingabezeile und fügen Sie jedemprinteinen Zeilenumbruch hinzu anrufen;-a:awk-mode, jede Eingabezeile in das Array@Faufteilen;-F:das Feldtrennzeichen für-a.
Ein wichtiger Unterschied zu awk ist das perl ist -a switch teilt Dateien in ein Array auf. In Perl beginnen Arrays bei 0, nicht bei 1. Das bedeutet, dass das zweite Feld eigentlich $F[1] ist und nicht $F[2][code> . Mit all dem im Hinterkopf, der perl Äquivalente der obigen sind:
-
Gibt Zeilen aus, deren drittes Feld
fooist .perl -ane 'print if $F[2] eq "foo"' fileÄnderung des Trennzeichens in
:perl -F":" -ane 'print if $F[2] eq "foo"' fileIm Gegensatz zu
awk,perlkann keine regulären Ausdrücke als Feldtrennzeichen verwenden. Sie müssen ein bestimmtes Zeichen oder eine Zeichenfolge sein. -
Wie kann ich nur Zeilen drucken, deren drittes Feld nicht
fooist ?perl -ane 'print unless $F[2] eq "foo"' file -
Wie kann ich nur Zeilen drucken, deren drittes Feld
fooentspricht ?perl -ane 'print if $F[2]=~/foo/' file -
Wie kann ich nur Zeilen drucken, deren drittes Feld nicht mit
fooübereinstimmt ?perl -lane 'print unless $F[2]=~/foo/' file -
Wie kann ich das 3. Feld in
fooändern ?Dieser ist in Perl etwas umständlicher. Der übliche Ansatz besteht darin, den Wert in
@Fzu ändern Array und drucken Sie dann das Array. Mit einfachen, durch Leerzeichen getrennten Dateien ist dies einfach:perl -lane '$F[2]="foo"; print "@F"' fileBei einem anderen Trennzeichen müssen Sie
beitretendas Array. Andernfalls wird es durch Leerzeichen getrennt gedruckt:perl -F: -lane '$F[2]="foo"; print join ":",@F' file
2. Verwenden Sie reguläre Ausdrücke
Die Idee dabei ist, einen regulären Ausdruck (kurz „Regex“) zu verwenden, der die Position des Zielstrings in der Zeile festlegt. Beispielsweise in einer Datei, deren Felder durch : getrennt sind , können wir das 2. Feld finden, indem wir alles bis zum 1. : abgleichen (das 1. Feld) und dann nach dem zweiten suchen:
^[^:]*:[^:]*:
Diese Regex bedeutet:
^:Anfang der Zeile;[^]:eine negierte Zeichenklasse.[^:]bedeutet „alles außer:“;*:0 oder mehr des vorherigen Musters;::ein wörtlicher:;
Zusammengenommen bedeutet dies, dass der erste [^:]* ist das erste Feld und das zweite ist das zweite Feld. Dies ist natürlich nicht sehr praktisch, wenn Sie nach dem 14. Feld suchen, aber es kann für einfachere Dinge nützlich sein. Wie implementieren wir das also, um unsere Daten zu manipulieren? Es gibt verschiedene Tools, die dies tun können; In diesen Beispielen verwende ich sed aber Sie könnten sehr ähnliche Dinge mit awk tun , perl oder python .
-
Wie kann ich nur Zeilen drucken, deren zweites Feld
fooist ?sed -n '/^[^:]*:foo:/p' fileDas
-nunterdrückt die normale Ausgabe und das/regex/pbedeutet „alle Zeilen ausgeben, die mit der Regex übereinstimmen. -
Wie kann ich nur Zeilen drucken, deren zweites Feld nicht
fooist ?sed '/^[^:]*:foo:/d' fileDie logische Umkehrung des Obigen. Hier der
/regex/dbedeutet „alle Zeilen löschen, auf die die Regex passt. -
Wie kann ich nur Zeilen drucken, deren 2. Feld
fooentspricht ?sed -n '/^[^:]*:[^:]*foo/p' file -
Wie kann ich nur Zeilen drucken, deren 2. Feld nicht mit
fooübereinstimmt ?sed '/^[^:]*:[^:]*foo/d' file -
Wie kann ich das 2. Feld in
fooändern ?sed 's/([^:]*:)[^:]*/1foo/' fileOder seit
sedSubstitution kann ein Mustervorkommen durch seine Wiederholung mit einem einfachen numerischen Flag direkt adressieren:sed 's/[^:]*/foo/2' file