2016-05-08 10 views
2

Ich habe eine Textdatei mit Daten getrennt durch 4 separate | Es gibt einige Problemzeilen in der Datei. Diese Zeilen enthalten weniger als 4 Pipes. Die Daten in den Problemzeilen werden nicht benötigt, und ich möchte einen Befehl für die Datei ausführen, der jede Zeile löscht, die weniger als vier Pipes enthält. Ich würde auch gerne wissen, wie viele Zeilen danach gelöscht wurden. Wenn dies auf dem Bildschirm gedruckt werden könnte, sobald der Befehl angewendet wird, wäre das ideal.Entfernen Sie Zeilen in Textdatei, die weniger als 4 Rohre enthalten

Beispieldaten:

865|Blue Moon Club|Havana Project|34d|879 
899|Soya Plates|Dimsby|78a|699 
657|Sherlock 
900|Forestry Commission|Eden Project|68d|864 

gewünschte Ausgabe:

865|Blue Moon Club|Havana Project|34d|879 
899|Soya Plates|Dimsby|78a|699 
900|Forestry Commission|Eden Project|68d|864 

ich versucht habe awk '|>=3' file.txt, die nicht funktioniert hat. Es gibt eine Menge von Informationen über awk, von denen ich einige gefunden habe, aber es gibt so viel, es macht es schwierig, genau zu finden, was ich wegen seiner schieren Menge tun möchte.

+0

Ich habe versucht awk ‚|> = 3‘ file.txt aber nicht – neilH

+0

Beispieldaten nicht funktioniert und die gewünschte Ausgabe hinzugefügt – neilH

+1

Siehe auch: [Zeile löschen, die mehr als X-Spalten in einer csv hat] (http://stackoverflow.com/q/29411952/3776858) – Cyrus

Antwort

7

die Linien zu beseitigen:

grep '|.*|.*|.*|' file > newfile 

die Anzahl der schlechten Zeilen zu zählen:

grep -cv '|.*|.*|.*|' file 

Das ist nicht die Bearbeitung an Ort und Stelle nicht tun; Sie konnte das mit sed tun, aber es ist oft sicherer Änderungen wie diese zu einem newfile zu tun, um zu verhindern, um Daten zu verlieren, wenn Sie einen Fehler machen.

Das erste grep-Muster entspricht einer Zeile mit vier Rohrsymbolen. (Standardmäßig verwendet grep „Basic“ reguläre Ausdrücke, in dem Sie den Wechsel Operator \| zu schreiben. Also Sie | als normales Zeichen verwenden können.)

Der zweite Aufruf zählt (-c) die Anzahl der Nicht- passende (-v) Linien.

Hier ist eine einfache sed Lösung:

sed -n -i.bak '/|.*|.*|.*|/p' file 

Die -n Option automatischen Druck schaltet sich aus, so dass der Befehl druckt nur die Linien, die das Muster entsprechen. (Wiederum verwendet sed standardmäßig grundlegende Regexes.). Die Option -i.bak führt die Bearbeitung aus und erstellt eine Sicherungskopie des Originals mit dem Namen file.bak.

Wenn Sie Linien auswählen mit genau vier Rohren wollten, könnten Sie awk verwenden:

awk -F'|' 'NF==5' file > newfile 

welche den eingereichten Separator auf ein Pipe-Symbol gesetzt und dann die Linien auswählen, mit genau fünf Feldern, die sind die Linien mit vier Pfeifen.

Ein nützliches Werkzeug Linien zu zählen ist wc:

wc -l file 

wird Ihnen sagen, wie viele Zeilen in der Datei sind; wenn Sie Linien in beiden file und newfile zählen, wird natürlich der Unterschied ist die Anzahl der Löschungen sein.Sie könnten diese Berechnung in awk auch tun, aber es ist ein bisschen wortreich:

awk -F'|' 'NF==5{print;next}{del+=1}END{print del >>"/dev/stderr"}' file > newfile 
+0

Die portable Möglichkeit, in awk nach stderr zu drucken, ist 'awk '... END {print del | "Katze> & 2"} ''. –

2

Dies tun:

sed -i.bak '/\([^|]*|\)\{4\}/!d' file 

oder (wie Cyrus's comment)

sed -i.bak -E '/(\|[^\|]*){4}/!d' file 

Oder

sed -n '/^[^|]*|[^|]*|[^|]*|[^|]*|$/p' file > newfile 

Oder

sed -e '/^[^|]*|[^|]*|[^|]*|$/d' \ 
    -e '/^[^|]*|[^|]*|$/d' \ 
    -e '/^[^|]*|$/d' \ 
    -e '/^[^|]*$/d' \ 
    -i.bak file 

Dies wird Ihnen jedoch keine Zeilenanzahl geben. Um Zeilenzahl laufen grep -cv '^[^|]*|[^|]*|[^|]*|[^|]*|$' file auf die Originaldatei als rici erwähnt zu erhalten, oder die Zeilennummer vergleichen, bevor und nachdem sie mit wc -l file Befehl


Erläuterung:

Die ersten beiden sed Matches locker 4 Rohre (nicht weniger aber kann mehr sein) und der dritte entspricht genau 4 | (nicht mehr oder weniger).

Der vierte sed Matches genau 3,2,1 und 0 Rohre (|) und löscht diese Zeilen (in place) und bereitet eine Sicherungsdatei (datei.bak) des Originals.

+1

oder mit GNU sed: 'sed -E/(\ | [^ \ |] *) {4} /! D 'file' – Cyrus

+0

@Cyrus: Ich war mir nicht sicher, ob Regex wie' ([ab] *) * 'funktioniert wirklich mit sed. Danke, dass du es aufgezeigt hast. – Jahid