Vergleichen Sie zwei Spalten mit unterschiedlichen Dateien und drucken Sie sie, wenn sie übereinstimmen

16

Ich verwende Solaris 10 und daher funktionieren die grep -Optionen mit -f nicht.

Ich habe zwei durch Pipes getrennte Dateien:

file1:

abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|

Datei 2:

abc|123|
kumar|pki|
cab|234

Ich möchte die ersten beiden Spalten von Datei2 mit Datei1 vergleichen (den gesamten Inhalt von Datei1 in den ersten beiden Spalten durchsuchen), wenn sie übereinstimmen, und die übereinstimmende Zeile von Datei1 drucken. Suchen Sie dann nach der zweiten Zeile von Datei 2 und so weiter.

Erwartete Ausgabe:

abc|123|BNY|apple|
cab|234|cyx|orange|

Die Dateien, die ich habe, sind riesig und enthalten ungefähr 400.000 Zeilen. Deshalb möchte ich die Ausführung schnell machen.

user68365
quelle
Ich habe die führenden Leerzeichen aus Ihren Beispielen entfernt. Wenn Sie dies wünschen, setzen Sie die Bearbeitung zurück. Denken Sie daran, dass Leerzeichen von Bedeutung sind. Sie sollten sie nur haben, wenn sie in Ihren tatsächlichen Dateien vorhanden sind.
Terdon
Versuchen Sie es mit der GNU-Version von grep, es ist unter /usr/sfw/bin/ggrep. stackoverflow.com/questions/15259882/…
slm

Antworten:

21

Dafür wurde awk entwickelt:

$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|

Erläuterung

  • -F'|': setzt das Feldtrennzeichen auf |.
  • NR==FNR: NR ist die aktuelle Eingabezeilennummer und FNR die Zeilennummer der aktuellen Datei. Die beiden sind nur gleich, während die erste Datei gelesen wird.
  • c[$1$2]++; next: Wenn dies die erste Datei ist, speichern Sie die ersten beiden Felder im cArray. Fahren Sie dann mit der nächsten Zeile fort, damit dies nur für die erste Datei gilt.

  • c[$1$2]>0: Der else-Block wird nur ausgeführt, wenn dies die zweite Datei ist. Wir prüfen daher, ob die Felder 1 und 2 dieser Datei bereits gesehen wurden ( c[$1$2]>0), und drucken die Zeile, wenn dies der Fall war . In awkist die Standardaktion das Drucken der Zeile. Wenn dies c[$1$2]>0zutrifft, wird die Zeile gedruckt.


Alternativ, da Sie mit Perl getaggt haben:

perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
         while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1

Erläuterung

Die erste Zeile öffnet sich file2, liest alles bis zum 2. |( .+?\|[^|]+) und speichert das (das $&ist das Ergebnis des letzten Match-Operators) im %kHash.

Die zweite Zeile verarbeitet file1 und verwendet denselben regulären Ausdruck, um die ersten beiden Spalten zu extrahieren und die Zeile zu drucken, wenn diese Spalten im %kHash definiert sind .


Beide oben genannten Ansätze müssen die 2 ersten Spalten von Datei2 im Speicher halten. Das sollte kein Problem sein, wenn Sie nur ein paar hunderttausend Zeilen haben, aber wenn ja, können Sie so etwas tun

cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done

Das wird aber langsamer.

terdon
quelle
Aber wird das nicht alles (die ersten beiden Spalten) file2in den Speicher laden?
Joseph R.
@terdon: awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0'ist kürzere Version.
Cuonglm
es funktioniert nicht ..
user68365
@ user68365: Gibt file2es doppelte Zeilen?
Cuonglm
NEIN, es hat keine doppelten Zeilen
user68365
1

Meiner Ansicht nach

grep -Ff file2 file1

ist was Sie suchen. Es sollte effizient sein, aber ich bin nicht sicher, ob es so genau ist, wie Sie es wollen. Befindet sich abc|123(zum Beispiel) in einer Zeile in file1verschiedenen Spalten, wird diese Zeile ebenfalls gedruckt. Wenn Sie garantieren können, dass dies niemals geschieht, sollte die obige Zeile funktionieren.

Joseph R.
quelle
Grep würde nicht ausreichen, da das abc | 123 irgendwo in der Datei vorhanden sein könnte. Außerdem benutze ich Solaris 10 und kann diese Grep-Option auch nicht verwenden.
User68365
2
@ user68365 bitte klären Sie dies alles in Ihrer Frage. Sie müssen uns Ihr Betriebssystem mitteilen und angeben, dass Sie nur die ersten beiden Spalten abgleichen möchten.
Terdon
1

Wenn Sie das Problem auf SQL-ähnliche Weise betrachten möchten, sollten Sie auf jeden Fall ein Tool mit dem Namen ' q ' ausprobieren :

$ q -d '|' "select f1.* from file1 f1 join file2 f2 on (f1.c1 = f2.c1 and f1.c2 = f2.c2)"

Es ist klarer und verständlicher, wenn Sie mit SQL-Abfragen vertraut sind.

Vincent
quelle
Vielen Dank für eine der mit Abstand am wenigsten kryptischen Lösungen. Das ist, was ich will. Aber ich hatte einige Probleme, dieses "Q-Tool" zu finden
Rolf
Sehr nützliches Werkzeug.
ghilesZ
0
$  sed 's/^/\^/' 2.txt > temp.txt ; grep 1.txt -f temp.txt
abc|123|BNY|apple|
cab|234|cyx|orange|
mr_tron
quelle
1
Wie ich in der Frage bearbeitet und erwähnt habe, funktioniert die Option grep -f in meinem System nicht
user68365
Solaris 10 hat einen Kern GNU-utils in / usr / SFW / bin Use / usr / SFW / bin / SED und / usr / SFW / bin / grep
mr_tron