Wie grep ich nach Zeilen, die einen bestimmten Wert in einer bestimmten Spalte haben?

9

Ich habe eine Datei wie folgt

  200.000    1.353    0.086
  200.250    1.417    0.000
  200.500    1.359    0.091
  200.750    1.423    0.000
  201.000    1.365    0.093
  201.250    1.427    0.000
  201.500    1.373    0.093
  201.750    1.432    0.000
  202.000    1.383    0.091
  202.250    1.435    0.000
  202.500    1.392    0.087
  202.750    1.436    0.000
  203.000    1.402    0.081
  203.250    1.437    0.001
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045  

Ich möchte nur die Zeilen erfassen, die in der ersten Spalte nur die Dezimalzahl .000 und .500 haben, damit die Ausgabe so aussieht

  200.000    1.353    0.086
  200.500    1.359    0.091
  201.000    1.365    0.093
  201.500    1.373    0.093
  202.000    1.383    0.091
  202.500    1.392    0.087
  203.000    1.402    0.081
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045  
Mohsen El-Tahawy
quelle
2
Es sieht einfach aus. Was hast du bisher versucht? Welche Probleme hatte Ihr Code?
John1024
Vielleicht ist es einfach für dich, aber ich habe es mit grep '.000' | versucht grep '.005', aber es sortiert auch die Zeilen, die den gleichen Wert in anderen Spalten haben
Mohsen El-Tahawy
3
Sehr gut. Die Leute hier sind viel sympathischer, wenn Sie einen ehrlichen Versuch zeigen, das Problem selbst zu lösen. Der Code in Ihrem Kommentar zeigt das. Wenn Sie in Zukunft solche Versuche in Ihre Frage aufnehmen, erhalten Sie wahrscheinlich schneller bessere Antworten.
John1024

Antworten:

14

Sie verwenden kein grep. Verwenden Sie awk.

"your data" | awk '$1 ~ /\.[05]00/'
azzid
quelle
Sehr gut. Wie geschrieben, hängt der Code davon ab, dass genau drei Stellen nach der Dezimalstelle stehen. Es wäre robuster zu bedienen awk '$1 ~ /\.[05]0*$/'.
John1024
1
@ John1024, tatsächlich hängt der Code wie geschrieben davon ab, dass mindestens drei Ziffern nach der Dezimalstelle stehen. Ich würde awk '$1 ~ /\.[05]00$/'mich zu mir selbst neigen (genau drei Ziffern benötigen), es sei denn, ich hätte Grund zu der Annahme, dass in der Eingabe variable Dezimalstellen erwartet werden.
Wildcard
2
@Wildcard Wenn mehr als drei vorhanden sind, schlägt der Code möglicherweise fehl. Zum Beispiel : echo 0.5001 | awk '$1 ~ /\.[05]00/'. Es funktioniert nur zuverlässig, wenn es genau drei gibt.
John1024
4
awk '$1 ~ /\.[50]00/ { print $0 }' myFile.txt

Die erste Spalte $1wird mit /\.500|\.000/den Punkten abgeglichen, die als wörtliche Punkte maskiert werden, die kein Zeichen wiedergeben, das ~teilweise übereinstimmt, und die gesamte Zeile drucken$0

Dalvenjia
quelle
2
Kein Grund aufzunehmen { print $0 }; Das ist Awks Standardaktion.
Wildcard
4

Ich möchte grep nur die Zeilen, die die dezimale in der ersten Spalte haben 0,000 und 0,500

Mein erster Gedanke

grep '^ *[0-9][0-9][0-9]\.[50]00' filename

Schnelltest mit WSL

$ head testdata
              200.000    1.353    0.086
              200.250    1.417    0.000
              200.500    1.359    0.091
              200.750    1.423    0.000
              201.000    1.365    0.093
              201.250    1.427    0.000
              201.500    1.373    0.093
              201.750    1.432    0.000
              202.000    1.383    0.091
              202.250    1.435    0.000
$ grep '^ *[0-9][0-9][0-9]\.[50]00' testdata
              200.000    1.353    0.086
              200.500    1.359    0.091
              201.000    1.365    0.093
              201.500    1.373    0.093
              202.000    1.383    0.091
              202.500    1.392    0.087
              203.000    1.402    0.081
              203.500    1.412    0.073
              204.000    1.423    0.065
              204.500    1.432    0.055
              205.000    1.441    0.045

Es gibt präzisere Möglichkeiten, dies auszudrücken.

$ grep -E '^ *[0-9]{3}\.[50]00' testdata
              200.000    1.353    0.086
              200.500    1.359    0.091
              201.000    1.365    0.093
              201.500    1.373    0.093
              202.000    1.383    0.091
              202.500    1.392    0.087
              203.000    1.402    0.081
              203.500    1.412    0.073
              204.000    1.423    0.065
              204.500    1.432    0.055
              205.000    1.441    0.045

Wenn die erste Spalte einen anderen als einen dreistelligen ganzzahligen Teil haben kann

grep -E '^ *[0-9]+\.[05]00' testdata

Unter bestimmten Umständen müssen Sie möglicherweise [:digit:]anstelle von verwenden [0-9].

Und so weiter.

man grep ist dein Freund.

RedGrittyBrick
quelle
Diese Verwendung von grepist einfacher zu verwenden als meine. Ich hätte keine Antwort gepostet, wenn ich diese zuerst gesehen hätte. Gut gemacht!
Yokai
2

Abhängig von Ihrem Anwendungsfall können Sie auch tatsächliche numerische Operationen verwenden:

$ awk '{a = $1 % 1} a == 0 || a == 0.5' /tmp/foo
  200.000    1.353    0.086
  200.500    1.359    0.091
  201.000    1.365    0.093
  201.500    1.373    0.093
  202.000    1.383    0.091
  202.500    1.392    0.087
  203.000    1.402    0.081
  203.500    1.412    0.073
  204.000    1.423    0.065
  204.500    1.432    0.055
  205.000    1.441    0.045

Getestet mit BSD awk (OSX El Capitan, 20070501) und GNU awk 4.1.4.

muru
quelle
1
Warnung: Das Testen der exakten Gleichheit des Gleitkommas (das von awk verwendet wird) führt häufig zu "falschen" Ergebnissen, es sei denn, die Werte haben keinen Bruchteil (und sind nicht zu groß) oder der Bruchteil ist "binär" (genau die Hälfte, a Quartal usw.), was für die Daten in diesem Q gilt, aber nicht für viele andere, die dem Uneingeweihten ähnlich erscheinen.
dave_thompson_085
1
@ dave_thompson_085 in der Tat, aber mit gawk können Sie Arithmetik mit beliebiger Genauigkeit verwenden , zugegebenermaßen verwende ich sie hier nicht.
Muru
2
 grep -e '2[^ ]*.000' -e '2[^ ]*.500' file.txt
Prinz 987
quelle
2

Mit awk:

$>awk '$1%.5==0' data.tsv 
200.000 1.353   0.086
200.500 1.359   0.091
201.000 1.365   0.093
201.500 1.373   0.093
202.000 1.383   0.091
202.500 1.392   0.087
203.000 1.402   0.081
203.500 1.412   0.073
204.000 1.423   0.065
204.500 1.432   0.055
205.000 1.441   0.045

Mit mlr:

$>mlr --ifs tab --onidx filter '$1%.5==0' data.tsv 
200.000 1.353 0.086
200.500 1.359 0.091
201.000 1.365 0.093
201.500 1.373 0.093
202.000 1.383 0.091
202.500 1.392 0.087
203.000 1.402 0.081
203.500 1.412 0.073
204.000 1.423 0.065
204.500 1.432 0.055
205.000 1.441 0.045
FloHimself
quelle
2

Ok, etwas spät in meinem Beitrag, aber ich denke, es lohnt sich.

Die Anforderung, die laut OP erfüllt werden muss, ist die erste Spalte mit dem Dezimalwert von .000oder .500nur. Es gibt keine Bestimmung bezüglich des Leitwerts, weder nach Bereich noch nach Länge. Für Robustheit sollte durch nichts außer , dass es keine nicht-leere Zeichen vor der ersten Spalte (oder es ist nicht mehr die erste Spalte) , und dass der Inhalt der ersten Spalte nicht davon ausgegangen werden , gezwungen wird , wird eine Dezimalstelle ., irgendwo drin.

Das OP möchte verwenden grep, wodurch die gesamte Zeile gedruckt wird, wenn eine Übereinstimmung gefunden wird. Das einzige, was zu tun ist, ist das Muster zu erstellen, das allen und nur dem entspricht, was erforderlich ist.

Die Einfachheit selbst und kein Grund, sedoder awkals `grep zu verwenden, kann die Quelle als Datei oder Pipe behandeln.

Um grepeine Datei zu verwendengrep '^[^.]*\.[05]0\{2\}\s' the_file.txt

Um grepvon einem Rohr zu verwendenmy_command | grep '^[^.]*\.[05]0\{2\}\s'

Das Muster lautet : ^, am Zeilenanfang beginnen; [^.], mit einem beliebigen Nicht-Dezimalzeichen übereinstimmen; *so oft wie möglich (einschließlich keiner); \.mit einem Dezimalpunkt übereinstimmen; [05]stimmen entweder mit einer Fünf oder einer Null überein; 0\{2\}, passen Sie 2 weitere Nullen an (die umgekehrten Schrägstriche vor dem Öffnen und Schließen der Klammer verhindern, dass die Shell versucht, eine Klammererweiterung durchzuführen); \s, entsprechen einem Leerzeichen (dh dem Ende der Spalte - um es in einem anderen Anwendungsfall zu verwenden, ersetzen Sie es durch das Spaltentrennzeichen, normalerweise ein Komman, ein Semikolon oder eine Registerkarte \t).

Beachten Sie, dass dies genau dem entspricht, was das OP verlangt hat. Es wird nicht übereinstimmen .5000oder .0000obwohl es numerisch äquivalent ist, da das Muster nach einer Fünf oder einer Null sucht, gefolgt von genau zwei weiteren Nullen, gefolgt von Leerzeichen. Wenn dies von Bedeutung ist, schlagen alle anderen Antworten bisher fehl, da sie nach der Testziffer mit einer beliebigen Anzahl von Nullen größer als 1 übereinstimmen. Und mit Ausnahme der Antwort von FloHimself stimmen sie mit allem in der zweiten Spalte überein, die beginnt, .000 oder .500, einschließlich .0003und .500T, und die von FloHimself stimmt mit allem überein, was mathematisch äquivalent zu .0und ist.5, egal wie viele Nullen es gibt. Der letzte entspricht zwar nicht den Angaben des OP, entspricht jedoch wahrscheinlich den Anforderungen des OP.

Wenn schließlich die Leistung und Geschwindigkeit von awkgewünscht wird, obwohl das OP dies verlangt grep, lautet der Befehl:

Mit einer Datei awk '$1 ~ /[^.]\.[05]0{2}$/' the_file.txt

Mit einer Pfeife my_command | awk '$1 ~ /[^.]\.[05]0{2}$/'

user207673
quelle
1

Wenn Sie darauf bestehen, grep zu verwenden, funktioniert dies möglicherweise für Sie. Ich habe die erste Ausgabe, die Sie bereitstellen, in einer Textdatei mit dem Namen "file.txt" gespeichert und dann den folgenden Befehl verwendet:

grep -e '2[^ ]*.000' file.txt & grep -e '2[^ ]*.500' file.txt

Welches ergibt eine Ausgabe von:

200.000    1.353    0.086
200.500    1.359    0.091
201.500    1.373    0.093
201.000    1.365    0.093
202.500    1.392    0.087
202.000    1.383    0.091
203.500    1.412    0.073
203.000    1.402    0.081
204.500    1.432    0.055
204.000    1.423    0.065
205.000    1.441    0.045

Sie müssen die Ausgabe nicht in einer Textdatei speichern, wenn sie sich bereits in einer Datei befindet. Falls es jedoch nicht in einer Datei gespeichert wird, können Sie die Daten auch in den von mir angegebenen Befehl grep weiterleiten. Es sollte mindestens bis zur ersten Nummer funktionieren 2, in der ersten Spalte steht nicht mehr a 2. Zu diesem Zeitpunkt müssen Sie den Befehl grep mit dem entsprechenden Zeichen aktualisieren, um korrekt zu drucken.

Was mit diesem doppelten grepBefehl passiert, ist, dass der erste grepmit dem &Bediener in den Hintergrund gesendet wird . Beim Senden an den Hintergrund wird der nächste grepBefehl unmittelbar danach ausgeführt, sodass Sie eine einheitliche Ausgabe erhalten. Damit die Aufgabe, die Sie erledigen müssen, einfacher erledigt werden kann, sollten Sie dem Beispiel folgen, das andere gegeben und verwendet haben awkoder sogar sed.

(bearbeiten)

Dies ist keineswegs die beste oder effektivste Verwendung von grep für Ihre Bedürfnisse, aber es sollte ausreichen, damit Sie ein bisschen herumspielen und ein besseres Gefühl für grep bekommen.

Yokai
quelle
Der erste Prozess wird im Hintergrund ausgeführt, jedoch nicht dämonisiert. Dazu gehört auch die Ausführung im Hintergrund, aber noch viel mehr. Und es ist sehr unwahrscheinlich, dass die Ausgabe in derselben Reihenfolge wie die Eingabe erfolgt. Selbst in Ihrem recht kleinen Beispiel ist es in der dritten Zeile bereits schief gelaufen.
dave_thompson_085
Er erwähnt nicht, dass die Ausgabe in einer bestimmten Reihenfolge erfolgen muss. Nur dass es spezifisch für das .500und .000der ersten Spalte sein muss. Wenn es in einer bestimmten Reihenfolge sein muss, z. B. am wenigsten bis am größten, kann dies leicht durchgeführt werden. Die ersten drei Ziffern der ersten zu druckenden Spalten sind jedoch in der kleinsten bis höchsten Reihenfolge. Das ist das Ergebnis des 2[^ ]*.000und 2[^ ]*.500. Es passt ziemlich gut zu dem, was das OP verlangt hat.
Yokai
Beachten Sie auch meinen Haftungsausschluss für Effizienzänderungen für den von mir angegebenen Befehl.
Yokai