Wie kann ich feldbasierte Daten über die Befehlszeile manipulieren? Zum Beispiel
- Wie kann ich nur Zeilen drucken, deren N-tes Feld
foo
ist ? - Wie kann ich nur Zeilen drucken, deren N-tes Feld nicht
foo
ist ? - 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
foo
ist .awk '$3=="foo"' file
Änderung des Trennzeichens in
:
awk -F":" '$3=="foo"' file
Die Standardaktion von
awk
ist zu drucken. Daher werden die obigen Befehle alle Zeilen drucken, deren drittes Feldfoo
ist . 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
foo
ist ?awk '$3!="foo"' file
-
Wie kann ich nur Zeilen drucken, deren drittes Feld
foo
entspricht ?Wenn Sie nur nach Feldern suchen, die einem Muster entsprechen (zum Beispiel
foo
entsprichtfoobar
), 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, dasperl
sollte laufen;-n
:liest die Eingabedatei Zeile für Zeile;-p
:gibt jede Eingabezeile aus, nachdem das durch-e
angegebene Skript angewendet wurde;-l
:Entfernen Sie abschließende Zeilenumbrüche aus jeder Eingabezeile und fügen Sie jedemprint
einen Zeilenumbruch hinzu anrufen;-a
:awk-mode, jede Eingabezeile in das Array@F
aufteilen;-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
foo
ist .perl -ane 'print if $F[2] eq "foo"' file
Änderung des Trennzeichens in
:
perl -F":" -ane 'print if $F[2] eq "foo"' file
Im Gegensatz zu
awk
,perl
kann 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
foo
ist ?perl -ane 'print unless $F[2] eq "foo"' file
-
Wie kann ich nur Zeilen drucken, deren drittes Feld
foo
entspricht ?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
@F
zu ändern Array und drucken Sie dann das Array. Mit einfachen, durch Leerzeichen getrennten Dateien ist dies einfach:perl -lane '$F[2]="foo"; print "@F"' file
Bei einem anderen Trennzeichen müssen Sie
beitreten
das 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
foo
ist ?sed -n '/^[^:]*:foo:/p' file
Das
-n
unterdrückt die normale Ausgabe und das/regex/p
bedeutet „alle Zeilen ausgeben, die mit der Regex übereinstimmen. -
Wie kann ich nur Zeilen drucken, deren zweites Feld nicht
foo
ist ?sed '/^[^:]*:foo:/d' file
Die logische Umkehrung des Obigen. Hier der
/regex/d
bedeutet „alle Zeilen löschen, auf die die Regex passt. -
Wie kann ich nur Zeilen drucken, deren 2. Feld
foo
entspricht ?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/' file
Oder seit
sed
Substitution kann ein Mustervorkommen durch seine Wiederholung mit einem einfachen numerischen Flag direkt adressieren:sed 's/[^:]*/foo/2' file