Zusammenführen zweier Textdateien mit AWK

Zusammenführen zweier Textdateien mit AWK

Ich habe nicht viel Erfahrung mit awk, daher habe ich Schwierigkeiten, ein Problem damit zu lösen. Ich habe zwei Dateien mit den Namen file1.txt und file2.txt. File1.txt:

20 101 1 2 3 4
20 102 5 6 7 8
20 108 3 3 3 3

Datei2.txt:

20 100 99 99 99 99
20 101 11 22 33 44
20 103 55 66 77 88

In jeder der Dateien stehen nach den ersten beiden Spalten immer 4 Werte.

Ich versuche, diese Dateien zu einer zusammenzuführen. Ich verbinde sie über die erste und zweite Spalte.

Die resultierende Datei sollte 10 Spalten enthalten. Die ersten beiden sind die Schlüsselspalten, die nächsten 4 Spalten sind Werte aus der ersten Datei und die letzten 4 sind aus der zweiten Datei.

In der resultierenden Datei enthält jeder Datensatz der ersten Datei, der keine Übereinstimmung mit der zweiten Datei aufweist (und umgekehrt), zusätzliche Nullen, die die fehlenden Werte darstellen.

Alles ist durch Leerzeichen getrennt.

Das Ergebnis sollte wie folgt aussehen:

20 100 0 0 0 0 99 99 99 99
20 101 1 2 3 4 11 22 33 44
20 102 5 6 7 8 0 0 0 0
20 103 0 0 0 0 55 66 77 88
20 108 3 3 3 3 0 0 0 0

Dies ist mein Awk-Skript, das ich aus einem Skript modifiziert habe, das ich bei meiner Suche im Internet gefunden habe:

BEGIN {                                                                                                                
   OFS=" "
}                                      
{                               
   i=$1 OFS $2 #Making key out of first and second field of the first file                    
}
   NR==FNR {                             
   A[i]=$0  #Saving records from first file to an array using first two columns as index                           
   next
}
#Next part assumes that I'm reading file2.txt                                         
i in A {                                
   printf "%s",A[i]  #Here, I have a match with first file, and I want to print the joined record I saved from file1.txt                                                                                               
   print $3,$4,$5,$6 #In order to print a joined record, after printing record from first file, I'm printing columns from the second file                                                                                           
   delete A[i]                           
   next
}

{ #Here I print records from file2.txt that don't have a match with file1.txt, and put zeroes to fill missing values
   print 0,0,0,0,$3,$4,$5,$6
}
END { #In the END block I'm printing everything from file1.txt that doesn't have a match and print zeroes aftewards to fill missing values
   for (i in A) {  printf "%s",A[i]; print 0,0,0,0  }                           
}

Das Ergebnis wird nach der zweiten Spalte sortiert und alle fehlenden Werte werden mit Nullen aufgefüllt. Das Ergebnis, das ich derzeit erhalte, sieht jedoch folgendermaßen aus:

20 100 0 0 0 0 99 99 99 99
11 22 33 443 4
20 103 0 0 0 0 55 66 77 88
20 108 3 3 3 3 0 0 0 0
0 0 0 0 6 7 8

Obwohl die Datei nicht sortiert ist (ich kann immer sort -k 2 verwenden), werden einige Zeilen nicht wie beabsichtigt gedruckt, und ich kann nicht erklären, warum es normalerweise kein Element eines Arrays A drucken kann. Ich habe verschiedene Dinge ausprobiert, wie z. B. das vorübergehende Ändern von ORS (überhaupt keine Ausgabe) oder die Verwendung von print anstelle von printf (das Ergebnis sieht auch seltsam aus).

Aufgrund mangelnder Erfahrung wirft dies einige zusätzliche Fragen auf:

Ist es sinnvoll, awk zu verwenden, um diese Aufgabe zu erledigen? Ich habe versucht,verbinden, blieb aber irgendwann hängen, weil es keine Spalte mit einem Zeilenumbruchzeichen am Ende drucken konnte. Vielleicht wäre ein Python-Skript nützlicher?

Ist es angesichts der Tatsache, dass ich zum Zusammenführen sehr große Dateien verwenden werde, hinsichtlich des Speichers sinnvoll, Arrays zu verwenden?

Dank im Voraus!

Antwort1

awk '!second { file1vals[$1 FS $2]=$0 }
      second { print (($1 FS $2 in file1vals)?file1vals[$1 FS $2]: $1 FS $2 FS "0 0 0 0") FS $3, $4, $5, $6;
               delete file1vals[$1 FS $2]
             }
END{ for(x in file1vals) print file1vals[x], "0 0 0 0" }' file1 second=1 file2

Dies funktioniert, solange genügend Speicher vorhanden ist, um die ersteDatei1in den Speicher.

Im ersten Block !second {...}, der nur ausgeführt wird, wenn es sich um die erste Datei handelt und wir dieDatei1in ein zugehöriges Array auf dem Paar aus 1. und 2. Spalte als Schlüssel für das Array.

Im zweiten Block second {...}, der nur ausgeführt wird, wenn es sich um die zweite Datei handelt, drucken wir die verbundenen Zeilen, die in beiden Dateien übereinstimmende Schlüssel haben, andernfalls drucken wir die Schlüssel und Nullen, gefolgt vom Rest der Spalten ausDatei2; dann löschen wir auch die Schlüssel aus dem Array, delete file1vals[$1 FS $2]wobei ihre Schlüssel in beiden Dateien vorhanden waren.

Im letzten Block am ENDE drucken wir die verbleibenden nicht übereinstimmenden Schlüssel aus, die sich aufDatei1.

verwandte Informationen