it-swarm.com.de

Wie entferne ich bestimmte Wörter aus Zeilen einer Textdatei?

meine textdatei sieht so aus:

Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

jetzt möchte ich Liquid penetration 95% mass (m) aus meinen Zeilen löschen, um nur die Werte zu erhalten. Wie soll ich das machen

13
O.E

Wenn es nur ein = -Zeichen gibt, können Sie alles vor und einschließlich = wie folgt löschen:

$ sed -r 's/.* = (.*)/\1/' file
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Wenn Sie die Originaldatei ändern möchten, verwenden Sie nach dem Testen die Option -i:

sed -ri 's/.* = (.*)/\1/' file

Anmerkungen

  • -r benutze ERE, damit wir nicht ( und ) entkommen müssen
  • s/old/new ersetze old durch new
  • .* beliebig viele beliebige Zeichen
  • (things) Speichern things, um später mit \1, \2 usw. eine Rückreferenz zu erstellen.
22
Zanna

Dies ist ein Job für awk; Angenommen, die Werte kommen nur im letzten Feld vor (gemäß Ihrem Beispiel):

awk '{print $NF}' file.txt
  • NF ist eine awk-Variable, die auf die Anzahl der Felder in einem Datensatz (Zeile) erweitert wird. Daher enthält $NF (beachten Sie den $ voran) den Wert des letzten Feld.

Beispiel:

% cat temp.txt 
Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

% awk '{print $NF}' temp.txt
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
22
heemayl

Mit grep und dem -P für PCRE (Interpretiere das Muster als P erl- C kompatibel R egular E xpression) und -o, um nur ein übereinstimmendes Muster zu drucken. Die Benachrichtigung \K ignoriert das vor sich gehende übereinstimmende Teil.

$ grep -oP '.*= \K.*' infile
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Oder Sie könnten stattdessen den Befehl cut verwenden.

cut -d= -f2 infile
13
αғsнιη

Ich habe mich entschlossen, die verschiedenen hier aufgeführten Lösungen zu vergleichen. Zu diesem Zweck habe ich eine große Datei erstellt, die auf dem vom OP bereitgestellten Inhalt basiert:

  1. Ich habe eine einfache Datei mit dem Namen _input.file_ erstellt:

    _$ cat input.file
    Liquid penetration 95% mass (m) = 0.000205348
    Liquid penetration 95% mass (m) = 0.000265725
    Liquid penetration 95% mass (m) = 0.000322823
    Liquid penetration 95% mass (m) = 0.000376445
    Liquid penetration 95% mass (m) = 0.000425341
    _
  2. Dann habe ich diese Schleife ausgeführt:

    _for i in {1..100}; do cat input.file | tee -a input.file; done
    _
  3. Terminalfenster wurde blockiert. Ich habe _killall tee_ von einem anderen Terminal aus ausgeführt. Dann überprüfte ich den Inhalt der Datei mit den Befehlen: _less input.file_ und _cat input.file_. Es sah gut aus, bis auf die letzte Zeile. Also habe ich die letzte Zeile entfernt und eine Sicherungskopie erstellt: _cp input.file{,.copy}_ (aufgrund der Befehle, die die Option inplace verwenden).

  4. Die endgültige Anzahl der Zeilen in der Datei _input.file_ beträgt 2 192 473 . Ich habe diese Nummer durch den Befehl wc erhalten:

    _$ cat input.file | wc -l
    2192473
    _

Hier ist das Ergebnis des Vergleichs:

  • grep -o '[^[:space:]]\+$'

     $ time grep -o '[^ [: space:]]\+ $' input.file> output.file 
     
     real 0m58.539s 
     user 0m58.416s 
     Sys 0m0.108s 
    
  • sed -ri 's/.* = (.*)/\1/'

     $ time sed -ris/.* = (. *)/\ 1/'input.file 
     
     real 0m26.936s 
     user 0m22. 836s 
     Sys 0m4.092s 
    

    Alternativ ist der Befehl schneller, wenn wir die Ausgabe in eine neue Datei umleiten:

     $ time sed -r 's/.* = (. *)/\ 1 /' input.file> output.file 
     
     real 0m19.734s 
     Benutzer 0m19.672s 
     sys 0m0.056s 
    
  • gawk '{gsub(".*= ", "");print}'

     $ time gawk '{gsub (". * =", ""); print}' input.file> output.file 
     
     real 0m5.644s 
     user 0m5.568s 
     sys 0m0.072s 
    
  • rev | cut -d' ' -f1 | rev

     $ time rev input.file | schneide -d '' -f1 | rev> output.file 
     
     real 0m3.703s 
     user 0m2.108s 
     sys 0m4.916s 
    
  • grep -oP '.*= \K.*'

     $ time grep -oP '. * =\K. *' input.file> output.file 
     
     real 0m3.328s 
     user 0m3.252s 
     sys 0m0.072s 
    
  • sed 's/.*= //' (bzw. die Option _-i_ macht den Befehl weniger mal langsamer)

     $ time sed 's /.*= //' input.file> output.file 
     
     real 0m3.310s 
     user 0m3.212s 
     sys 0m0.092s 
    
  • Perl -pe 's/.*= //' (die Option _-i_ erzeugt kein großes Ergebnis Unterschied in der Produktivität hier)

     $ time Perl -i.bak -pe 's/.*= //' input.file 
     
     real 0m3.187s 
     user 0m3.128s 
     sys 0m0.056s 
    
     $ time Perl -pe 's /.*= //' input.file> output.file 
     
     real 0m3.138s 
     user 0m3.036s 
     sys 0m0.100s 
    
  • awk '{print $NF}'

     $ time awk '{print $ NF}' input.file> output.file 
     
     real 0m1.251s 
     user 0m1.164s 
     sys 0m0.084s 
    
  • cut -c 35-

     $ time cut -c 35- input.file> output.file 
     
     real 0m0.352s 
     user 0m0.284s 
     sys 0m0. 064er 
    
  • cut -d= -f2

     $ time  cut -d = -f2  input.file> output.file 
     
      real 0m0.328s 
     user 0m0.260s 
     sys 0m0.064s 

Die Quelle der Idee.

13
pa4080

Da das Zeilenpräfix immer dieselbe Länge hat (34 Zeichen), können Sie cut verwenden:

cut -c 35- < input.txt > output.txt
11
David Foerster

Kehren Sie den Inhalt der Datei mit rev um, leiten Sie die Ausgabe in cut mit Leerzeichen als Trennzeichen und 1 als Zielfeld um und kehren Sie sie dann erneut um, um die ursprüngliche Nummer zu erhalten:

$ rev your_file | cut -d' ' -f1 | rev
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
6
f1nan

Dies ist einfach, kurz und leicht zu schreiben, zu verstehen und zu überprüfen, und ich persönlich mag es:

grep -oE '\S+$' file

grep in Ubuntu , wenn mit -E oder -P aufgerufen, bedeutet das Kürzel\s ein Leerzeichen (in der Praxis normalerweise ein Leerzeichen oder ein Tabulator) und \S, um etwas zu bedeuten, das nicht eins ist. Mit dem Quantifizierer + und dem Zeilenende-Anker $ stimmt das Muster \S+$ mit einem oder mehreren überein Nicht-Leerzeichen am Ende einer Zeile . Sie können -P anstelle von -E verwenden. Die Bedeutung in diesem Fall ist dieselbe, aber es wird eine andere reguläre Ausdrucks-Engine verwendet , sodass sie unterschiedliche Leistungsmerkmale haben können .

Dies entspricht Avinash Rajs kommentierter Lösung (nur mit einer einfacheren, kompakteren Syntax):

grep -o '[^[:space:]]\+$' file

Diese Ansätze funktionieren nicht, wenn nach der Zahl ein Leerzeichen nach stehen könnte. Sie können geändert werden, aber ich sehe keinen Grund, hier darauf einzugehen. Obwohl es manchmal lehrreich ist, eine Lösung zu verallgemeinern, um in mehreren Fällen zu arbeiten, ist es nicht praktikabel, dies fast so oft zu tun, wie die Leute annehmen, weil man normalerweise keine Möglichkeit hat, auf welche von vielen verschiedenen inkompatiblen Arten zu wissen Das Problem muss möglicherweise verallgemeinert werden.


Leistung ist manchmal ein wichtiger Gesichtspunkt. Diese Frage besagt nicht, dass die Eingabe sehr umfangreich ist, und es ist wahrscheinlich, dass jede hier veröffentlichte Methode schnell genug ist. Für den Fall, dass Geschwindigkeit gewünscht wird, finden Sie hier einen kleinen Benchmark für eine Eingabedatei mit zehn Millionen Zeilen:

$ Perl -e 'print((<>) x 2000000)' file > bigfile
$ du -sh bigfile
439M    bigfile
$ wc -l bigfile
10000000 bigfile
$ TIMEFORMAT=%R
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
819.565
$ time grep -oE '\S+$' bigfile > bigfile.out
816.910
$ time grep -oP '\S+$' bigfile > bigfile.out
67.465
$ time cut -d= -f2 bigfile > bigfile.out
3.902
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
815.183
$ time grep -oE '\S+$' bigfile > bigfile.out
824.546
$ time grep -oP '\S+$' bigfile > bigfile.out
68.692
$ time cut -d= -f2 bigfile > bigfile.out
4.135

Ich habe es zweimal ausgeführt, für den Fall, dass die Reihenfolge wichtig ist (wie es manchmal für E/A-schwere Aufgaben der Fall ist), und weil ich keine Maschine zur Verfügung hatte, die keine anderen Aufgaben im Hintergrund ausführte, die die Ergebnisse verzerren könnten. Aus diesen Ergebnissen schließe ich zumindest vorläufig und für Eingabedateien der von mir verwendeten Größe Folgendes:

  • Beeindruckend! Die Übergabe von -P (zur Verwendung von PCRE ) anstelle von -G (die Standardeinstellung, wenn kein Dialekt angegeben ist) oder -E beschleunigte grep um eine Größenordnung. Für große Dateien ist es möglicherweise besser, diesen Befehl zu verwenden als den oben gezeigten:

    grep -oP '\S+$' file
  • WOW !! Die cut-Methode in αғsнιηs Antwort , cut -d= -f2 file, ist um eine Größenordnung schneller als die schnellere Version meines Weges! Es war auch der Gewinner in pa4080s Benchmark , der mehr Methoden als diese, aber mit geringerem Input abdeckte - und aus diesem Grund habe ich es von allen anderen Methoden ausgewählt, um es in meinen Test aufzunehmen. Wenn die Leistung wichtig ist oder die Dateien sehr groß sind, sollte meines Erachtens α /sнιηs cut -Methode verwendet werden.

    Dies dient auch als Erinnerung, dass die einfachen cut und paste Dienstprogramme nicht vergessen werden sollten und Sollte bei Bedarf vielleicht vorgezogen werden, auch wenn es komplexere Tools wie grep gibt, die häufig als First-Line-Lösungen angeboten werden (und an deren Verwendung ich persönlich eher gewöhnt bin).

5
Eliah Kagan

Perl - s ersetzen Sie das Muster /.*= / durch eine leere Zeichenfolge //:

Perl -pe 's/.*= //' input.file > output.file
Perl -i.bak -pe 's/.*= //' input.file
  • Aus Perl --help:

    -e program        one line of program (several -e's allowed, omit programfile)
    -p                assume loop like -n but print line also, like sed
    -i[extension]     edit <> files in place (makes backup if extension supplied)
    

sed - Ersetze das Muster durch eine leere Zeichenkette:

sed 's/.*= //' input.file > output.file

oder (aber langsamer als oben) :

sed -i.bak 's/.*= //' input.file
  • Ich erwähne diesen Ansatz, weil er einige Male schneller ist als der in Zannas Antwort .

gawk - Ersetzen Sie das Muster ".*= " durch eine leere Zeichenfolge "":

gawk '{gsub(".*= ", "");print}' input.file > output.file
  • Aus man gawk:

    gsub(r, s [, t]) For each substring matching the regular expression r in the string t,
                     substitute the string s, and return the number of substitutions. 
                     If t is not supplied, use $0...
    
4
pa4080