Wie kann ich feldbasierte Daten über die Befehlszeile bearbeiten? Zum Beispiel
- Wie kann ich nur Zeilen drucken, deren N-tes Feld ist
foo
? - Wie kann ich nur Zeilen drucken, deren N-tes Feld nicht ist
foo
? - Wie kann ich nur Zeilen drucken, deren N-tes Feld übereinstimmt
foo
? - Wie kann ich Feld N in ändern
foo
?
Gibt es einen Standardansatz oder ein Toolset, mit dem feldbasierte Daten auf * nix-Systemen bearbeitet werden können?
text-processing
sed
awk
perl
terdon
quelle
quelle
Antworten:
Es gibt zwei grundlegende Ansätze, die beim Umgang mit Feldern verwendet werden können: i) Verwenden eines Tools, das Felder versteht; ii) Verwenden Sie einen regulären Ausdruck. Von den beiden ist der erstere normalerweise sowohl robuster als auch einfacher.
Viele der allgemein verfügbaren Tools auf * nix sind entweder explizit für den Umgang mit Feldern konzipiert oder verfügen über raffinierte Tricks, um dies zu vereinfachen.
1. Verwenden Sie ein Tool, das Felder versteht
1,1 awk
Das klassische Werkzeug hier ist
awk
. Es wird automatisch jede Eingangsleitung in Felder aufgeteilt (die Feldtrennstandardmäßig Leerzeichen sondern kann mit der ändernden-F
Flag) und die Felder sind dann an dasawk
Skript als wo die Feldnummer. Das erste Feld ist , das zweite usw.$n
n
$1
$2
Drucken Sie Zeilen, deren 3. Feld ist
foo
.Ändern des Trennzeichens in
:
Die Standardaktion von
awk
ist das Drucken. Daher drucken die obigen Befehle alle Zeilen, deren 3. Feld istfoo
. Bei der Verwendung-F
können Sie beliebige Feldtrennzeichen festlegen und sogar reguläre Ausdrücke verwenden.Wie kann ich nur Zeilen drucken, deren 3. Feld nicht ist
foo
?Wie kann ich nur Zeilen drucken, deren 3. Feld übereinstimmt
foo
?Wenn Sie nur nach Feldern suchen, die einem Muster entsprechen (z. B.
foo
Übereinstimmungenfoobar
), verwenden Sie~
anstelle von==
:Wie kann ich nur Zeilen drucken, deren 3. Feld nicht übereinstimmt
foo
?Wie kann ich das 3. Feld ändern
foo
?1,2 Perl
Eine andere Wahl ist
perl
Einzeiler. Wie awk ist Perl eine voll funktionsfähige Skriptsprache, kann aber auch als Befehlszeilenprogramm ausgeführt werden, das ein Skript als Eingabe verwendet. Sein Verhalten wird durch Befehlszeilenschalter geändert, von denen die relevantesten für diese Frage sind:-e
: das Skript, das ausgeführt werdenperl
soll;-n
: Lesen Sie die Eingabedatei Zeile für Zeile;-p
: drucke jede Eingabezeile nach dem Anwenden des Skripts von-e
;-l
: Entfernen Sie nachfolgende Zeilenumbrüche aus jeder Eingabezeile und fügen Sie jedemprint
Anruf eine neue Zeile hinzu .-a
: awk-mode, teile jede Eingabezeile in das Array auf@F
;-F
: das Feldtrennzeichen für-a
.Ein wichtiger Unterschied
awk
besteht darin, dassperl
der-a
Switch Dateien in ein Array aufteilt. In Perl beginnen Arrays bei 0, nicht bei 1. Dies bedeutet, dass das 2. Feld tatsächlich ist$F[1]
und nicht$F[2]
. In Anbetracht dessen sind dieperl
Äquivalente der oben genannten:Drucken Sie Zeilen, deren 3. Feld ist
foo
.Ändern des Trennzeichens in
:
Im Gegensatz zu
awk
,perl
kann keine reguläre Ausdrücke als Feldtrennzeichen verwenden. Sie müssen ein bestimmtes Zeichen oder eine bestimmte Zeichenfolge sein.Wie kann ich nur Zeilen drucken, deren 3. Feld nicht ist
foo
?Wie kann ich nur Zeilen drucken, deren 3. Feld übereinstimmt
foo
?Wie kann ich nur Zeilen drucken, deren 3. Feld nicht übereinstimmt
foo
?Wie kann ich das 3. Feld ändern
foo
?Dieser ist in Perl etwas umständlicher. Der übliche Ansatz besteht darin, den Wert im
@F
Array zu ändern und dann das Array zu drucken. Mit einfachen, durch Leerzeichen getrennten Dateien ist dies einfach:Mit einem anderen Trennzeichen müssen Sie
join
das Array. Andernfalls wird es durch Leerzeichen getrennt gedruckt:2. Verwenden Sie reguläre Ausdrücke
Die Idee hier ist, einen regulären Ausdruck (kurz "Regex") zu verwenden, der die Position der Zielzeichenfolge in der Zeile definiert. Zum Beispiel
:
können wir in einer Datei, deren Felder durch getrennt sind, das 2. Feld finden, indem wir alles bis zum 1.:
(dem 1. Feld) abgleichen und dann nach dem zweiten suchen:Dieser reguläre Ausdruck bedeutet:
^
: der Anfang der Zeile;[^]
: eine negierte Zeichenklasse.[^:]
bedeutet "alles andere als:
";*
: 0 oder mehr des vorherigen Musters;:
: ein wörtliches:
;Zusammengenommen bedeutet dies, dass das erste
[^:]*
das erste Feld und das zweite das zweite Feld ist. 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 dies, um unsere Daten zu manipulieren? Es gibt verschiedene Tools, die dies tun können. in diesen Beispielen werde ich verwenden ,sed
aber Sie tun können , sehr ähnliche Dinge mitawk
,perl
oderpython
.Wie kann ich nur Zeilen drucken, deren 2. Feld ist
foo
?Das
-n
unterdrückt die normale Ausgabe und/regex/p
bedeutet "alle Zeilen drucken, mit denen der reguläre Ausdruck übereinstimmt".Wie kann ich nur Zeilen drucken, deren 2. Feld nicht ist
foo
?Die logische Umkehrung des Obigen. Hier
/regex/d
bedeutet das "alle Zeilen löschen, mit denen der reguläre Ausdruck übereinstimmt".Wie kann ich nur Zeilen drucken, deren 2. Feld übereinstimmt
foo
?Wie kann ich nur Zeilen drucken, deren 2. Feld nicht übereinstimmt
foo
?Wie kann ich das 2. Feld in ändern
foo
?Oder, da die
sed
Substitution ein Auftreten von Mustern durch ihre Wiederholung mit einem einfachen numerischen Flag direkt ansprechen kann:quelle
Capt. Kirk, Mr. Spock, "Dr. McCoy, MD", Scotty
. (2) Ich bin beunruhigt über die Tatsache, dass (i) → 2 und (ii) → 1 , aber ich wollte nichts so Wichtiges ändern. (3) Wenn dies als kanonische Frage nominiert wird, sollte es dann nicht auf einige gründliche und maßgebliche Verweise auf reguläre Ausdrücke verweisen (z. B. Wikipedia , Regular-Expressions.info , RegexPlanet usw.)?s/:[^:]*/:foo/
auch., Aber das Backref ist es wahrscheinlich auch wert, in einen kanonischen Beitrag über Regexp aufgenommen zu werden.sed
kann die eingebetteten Trennzeichen ausführen, und wirklich jedes voll BRE-fähige Tool kann dies auch. Mit dieser Liste gaben Sie, tun:sed 's/[^,"]*\("[^"]*\)\{0,1\}[^,]*,//;s///2' <list
gedruckt wirdMr. Spock, Scotty
(mit einem führenden Leerzeichen) , weil der erste Wechsel das erste Feld entfernt, und die zweiten entfernt Knochen.Unlike awk, perl can't use regular expressions as field delimiters.
- dies gilt nur, wenn Sie die-a
automatische Aufteilung verwenden. Sie können sich trotzdem benutzen,split()
wenn Sie wollen. zBperl -p -e 'my @F = split /regexp/ ; print if $F[2] =~ /foo/'