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

Wie kann man Zeilen in einer Textdatei extrahieren/ändern, deren Daten in Felder aufgeteilt sind?

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 Feld foo 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 entspricht foobar ), 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, das perl 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 jedem print 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
    

Linux
  1. Mehrere Zeilen in eine durch Komma getrennte Zeile umwandeln

  2. Wie extrahiert man den Textteil einer Binärdatei in Linux/Bash?

  3. Wie kopiert man die Zeilen 10 bis 15 einer Datei in eine andere Datei in Unix?

  4. Wie konvertiert man bestimmten Text aus einer Liste in Großbuchstaben?

  5. Wie zeigt man bestimmte Zeilen aus einer Textdatei in Linux an?

Bash-Scripting:So lesen Sie Daten aus Textdateien

So drucken Sie doppelte Zeilen in einer Textdatei unter Linux

So verbinden Sie mehrere Zeilen in einer Datei in Linux zu einer

So extrahieren Sie E-Mail-Adressen aus einer Textdatei unter Linux

So importieren Sie Daten in Apache Solr

Wie Echo in Datei