Grep Zeile mit 0 aber nicht 0,2 entfernen?

12

Ich habe eine Datei, deren Inhalt dem folgenden ähnlich ist.

0
0
0.2
0
0
0
0

Ich muss alle Zeilen mit einer einzigen Null entfernen.
Ich habe überlegt zu verwenden grep -v "0", aber dies entfernt auch die Zeile mit 0,2. Ich habe gesehen, dass ich die -wOption verwenden könnte, aber das scheint auch nicht zu funktionieren.

Wie kann ich alle Zeilen entfernen, die nur eine einzige 0 enthalten, und alle diese Zeilen mit einer 0 beginnen lassen?

Philip Kirkbride
quelle
2
Mögliches Duplikat von Match genaue Zeichenfolge mit grep
Julien Lopez
1
@ JulienLopez Es ist kein Betrug dieser Frage. Bei dieser Frage geht es darum, ein Wort zu finden und mit zu beantworten -w, was hier fehlschlägt.
Sparhawk
Warum sind Sie gezwungen, grepfür diese Aufgabe zu verwenden? Und was genau meinst du mit einer einzigen Null ? Das klingt sehr nach einem XY-Problem .
Roland Illig
1
@ RolandIllig Es war 1 Stunde vor dem Schlafengehen und ich wollte eine Reihe von 500.000 Zeichenfolgen verarbeiten, um zu überprüfen, ob es sich um private Bitcoin-Schlüssel handelt, und wenn ja, um das Gleichgewicht zu erhalten. Als ich das nächste Mal Zeit hatte, es mir anzusehen, hatte ich viele tausend Zeichenfolgen verarbeitet und wollte nur nach Werten ungleich Null suchen.
Philip Kirkbride

Antworten:

35
grep -vx 0

Von man grep:

-x, --line-regexp
       Select only those matches that exactly match the whole line.
       For a regular expression pattern, this is like parenthesizing
       the pattern and then surrounding it with ^ and $.

-wschlägt fehl, weil das erste 0in 0.02als "Wort" betrachtet wird und daher diese Zeile übereinstimmt. Dies liegt daran, dass auf ein "Nicht-Wort" -Zeichen folgt. Sie können dies sehen, wenn Sie den ursprünglichen Befehl ohne ausführen -v, dh grep -w "0".

Sparhawk
quelle
Sie können die -FOption auch verwenden, da wir keine Regex-Muster verwenden, sondern nur einen einfachen String-Matching
Glenn Jackman,
@glennjackman Vielleicht habe ich das früher gelesen, aber ich kann es jetzt nicht finden. Das Laufen mit -F(für mich überraschend) scheint ähnlich lange oder sogar etwas langsamer zu dauern (~ 5–10%). Daher bin ich mir nicht sicher, was der Vorteil wäre.
Sparhawk
2
Es ist möglich, dass die RegEx-Engine so oft und so häufig verwendet wird, dass sie eine sehr effiziente Version davon implementiert hat, aber dass eine "einfache Suche" wahrscheinlich seit 30 Jahren nicht mehr aktualisiert wurde.
Nelson
@Sparhawk: grepVermutlich gibt es einen Sonderfall für Regexes ohne Metazeichen, da dies ein häufiger Anwendungsfall ist. Es ist überraschend, dass fgrepdies langsamer wäre, aber es ist nicht überraschend, dass der Aufwand für das Erkennen dieses Sonderfalls beim Kompilieren eines kurzen Musters im Vergleich zur Zeit zum Scannen einer großen Datei vernachlässigbar ist. (Wenn es überhaupt einen Sonderfall erfordert, um so schnell zu gehen, gegen ein Muster mit einer Charakterklasse oder x.*y.)
Peter Cordes
Aber das ist vielleicht eine Vereinfachung, weil die Eingabe tatsächlich viele kurze Zeilen ist (nicht eine riesige Zeichenfolge). Ich vergesse, ob grepein anderes Zeichen als \nZeilenumbruch als Zeilentrennzeichen erkannt wird . Wenn nicht, kann das implizite ^und $ dennoch zu einer Suche mit festen Zeichenfolgen werden strstr(big_buf, "\n0\n"). (Oder 0\nzu Beginn eines Puffers.) Wir suchen jedoch nicht nur nach der ersten Übereinstimmung, die möglicherweise weit in einem großen Puffer liegt, sondern möchten auch effizient filtern. Aber theoretisch ist es nur ein 2-Byte-Memcmp am Anfang jeder Zeile, und Sie würden hoffen, dass sowohl fgrep als auch grep das sehen würden.
Peter Cordes
28

Mit grep:

grep -v "^0$" file

^bedeutet Zeilenanfang, $bedeutet Zeilenende.

Arkadiusz Drabczyk
quelle
2
Dies ist, was der Benutzer verlangt hat: Vermeiden Sie Zeilen, die nur 1 "0" enthalten.
Olivier Dulac
1
Ich würde kein wörtliches Dollarzeichen in solche doppelten Anführungszeichen setzen.
user541686
@mehrdad nicht so großes Problem mit Regex, da es normalerweise entweder das letzte [a-Z0-9]
Zeichen
14

Während grep kann dafür verwendet werden (wie andere Antworten zeigen deutlich), lassen Sie uns einen Schritt zurück und überlegen , was Sie eigentlich wollen:

  • Sie haben eine Datei mit Zahlen
  • Sie möchten eine Filterung basierend auf dem numerischen Wert durchführen .

Regex interpretiert Zeichensequenzdaten. Sie kennen keine Zahlen, nur einzelne Ziffern (und reguläre Kombinationen davon). Obwohl es in Ihrem speziellen Fall einen einfachen Hack um diese Einschränkung gibt, handelt es sich letztendlich um eine Nichtübereinstimmung der Anforderungen.

Sofern es keinen sehr guten Grund gibt, grephier zu verwenden (z. B. weil Sie es gemessen haben und es wesentlich effizienter ist und Effizienz in Ihrem Fall von entscheidender Bedeutung ist), empfehle ich die Verwendung eines anderen Tools.

awkkann beispielsweise basierend auf numerischen Vergleichen filtern, z.

awk '$1 == 0' your_file

Aber auch, um alle Zeilen zu erhalten, die Zahlen größer als Null enthalten:

awk '$1 > 0' your_file

Ich liebe Regex, es ist ein großartiges Werkzeug. Aber es ist nicht das einzige Werkzeug. Wie das Sprichwort sagt, wenn alles grep, was Sie haben, ist , sieht alles wie eine normale Sprache aus.

Konrad Rudolph
quelle
3
Ich stimme voll und ganz zu, dass awk hier eleganter sein kann ... aber es passt vielleicht auch ein bisschen mehr als vom Benutzer erwartet (jeder numerische Wert wird mit 0 bewertet). Das heißt, printf '0\n1\n-1\na\nb\n0\n0 also\n0.0\n-0.0\n0*0\n' | awk '($1 == 0)'passt auf : 0, 0.0und -0.0... und auch 0 also! Nicht nur "0". (was manchmal benötigt wird, manchmal nicht). Wenn der Benutzer nur "0" möchte: awk '/^0$/' (oder grep '^0$'). Außerdem sollten Sie Folgendes bearbeiten: Der Benutzer muss hinzufügen !, um den Test zu negieren, damit er 0(und andere Nullen) ausblendet und den Rest anzeigt. dh:awk '!( $0 == 0)'
Olivier Dulac
1
@Olivier, oder überprüfen Sie den Zeichenfolgenwert:$1 == "0"
Glenn Jackman
1
@OlivierDulac Ich habe explizit >und nicht !=(oder gleichwertig ! (… == …)) verwendet, um hervorzuheben, dass dies ein willkürlicher numerischer Vergleich ist, nicht nur Gleichheit. Was Ihren anderen Kommentar betrifft, so ist dies völlig richtig, aber dann befinden wir uns im Wesentlichen wieder im Bereich des Zeichenfolgenvergleichs und der vorhandenen Lösung, die grepWerke verwendet (obwohl dies awknatürlich auch funktioniert).
Konrad Rudolph
@KonradRudolph faire Punkte :)
Olivier Dulac
1
@glennjackman: schöner Trick in der Tat. Aber dann würde OP lieber testen$0=="0"
Olivier Dulac
5

grep's -wist etwas verworren, so dass die ursprüngliche Zeichenfolge in Wort- und Nichtwortbestandteile (alles außer Buchstaben, Ziffern oder Unterstrichen) aufgeteilt wird. Da es bereits aa gültiges Wort Bestandteil gestoßen 0in 0.02hatte es die Negation Logik behauptet die Zeile zu entfernen.

Die Verwendung sedist in diesem Zusammenhang etwas einfach, um nur die gesamten übereinstimmenden Wörter zu entfernen

sed '/^0$/d' file
Inian
quelle
3

Wenn die zu löschenden Zeilen nur eine 0 gefolgt von der nächsten Zeile enthalten , können Sie diese Zeilen mit dem folgenden Befehl auswählen:

grep -v "^0$"

Dadurch werden nur die Vorkommen gedruckt, die 0sich gleichzeitig am Ende einer Zeile und am Anfang einer Zeile befinden . Die -vOption kehrt dann unsere Auswahl um.

majesticLSD
quelle
1
Diese Antwort ist fast identisch mit der von Arkadiusz Drabczyk, aber Sie haben sie vergessen -v, also funktioniert sie nicht.
Sparhawk
Du hast recht. Ich habe getippt, während er seine Antwort gepostet hat, also habe ich nicht gesehen, dass sie bereits gegeben wurde. Ich habe diesen Teil mit der -vOption falsch verstanden , danke!
majesticLSD
0
  • \ b - Wortgrenze

grep -v "\b0\b"

  • Passen Sie den Zeilenanfang, Ihr Muster und das Zeilenende an

grep -v "^0$"

  • oder wie von @Sparhawk vorgeschlagen -vx lineregexp

-w funktioniert, aber in Ihrem Fall sind 0,2 zwei Wörter, da das Punktzeichen ein Worttrennzeichen ist.

Jakub Jindra
quelle
grep -v "\b0\b"funktioniert hier nicht wirklich. Welche Version von grep verwenden Sie?
Arkadiusz Drabczyk
arbeitet mit grep (BSD grep) 2.5.1-FreeBSDunter MacOS und grep (GNU grep) 2.16Ubuntu
Jakub Jindra
1
GNU Regex verwenden \<und \>als Wortgrenzen, aber das wird den gleichen Effekt haben wie-w
Glenn Jackman
0

Eine weitere Antwort aus Gründen der Abwechslung, vorausgesetzt, Sie haben eine PCRE-fähige Funktion grep

grep -Pv "^0(?!\.)"

Dies führt einen negativen Lookahead durch , der mit den Linien übereinstimmt, die mit einem Punkt beginnen 0und auf den kein Punkt folgt. Dann werden -vnicht übereinstimmende Zeilen verworfen. Sie können hier in Aktion sehen

mrbolichi
quelle
1
Dies wird auch Zeilen wie entfernen 0123, was nicht das ist, was das OP will
iruvar
0

Angenommen, jede Zeile, die nicht nur eine einzelne 0 ist, hat einen Punkt

grep '\.' file

Roger Mungo
quelle