Verarbeitung von zwei Dateien mit awk

9

Ich habe den Vergleich zweier Dateien mit Unix und Awk gelesen . Es ist wirklich interessant. Ich habe es gelesen und getestet, aber ich kann es nicht vollständig verstehen und in anderen Fällen verwenden.

Ich habe zwei Dateien. file1hat ein Feld und das andere hat 16 Felder. Ich möchte Elemente von file1 lesen und sie mit dem 3. Feld von vergleichen file2. Wenn es für jedes Element eine Übereinstimmung gab, summiere ich den Wert von Feld 5 in file2. Als Beispiel:

Datei 1

1
2
3

Datei 2

2 2 2 1 2
3 6 1 2 4 
4 1 1 2 3
6 3 3 3 4 

Für Element 1 in file1möchte ich Werte in Feld 5 hinzufügen, file2wobei der Wert von Feld 3 1 ist. Und machen Sie dasselbe für Element 2 und 3 in file1. Die Ausgabe für 1 ist (3 + 4 = 7) und für 2 ist 2 und für 3 ist 4.

Ich weiß nicht, wie ich es mit awk schreiben soll.

user55340
quelle

Antworten:

20

Hier ist eine Möglichkeit. Ich habe es als awk-Skript geschrieben, damit ich Kommentare hinzufügen kann:

#!/usr/local/bin/awk -f

{
    ## FNR is the line number of the current file, NR is the number of 
    ## lines that have been processed. If you only give one file to
    ## awk, FNR will always equal NR. If you give more than one file,
    ## FNR will go back to 1 when the next file is reached but NR
    ## will continue incrementing. Therefore, NR == FNR only while
    ## the first file is being processed.
    if(NR == FNR){
      ## If this is the first file, save the values of $1
      ## in the array n.
      n[$1] = 0
    }
    ## If we have moved on to the 2nd file
    else{
      ## If the 3rd field of the second file exists in
      ## the first file.
      if($3 in n){
        ## Add the value of the 5th field to the corresponding value
        ## of the n array.
        n[$3]+=$5
      }
    }
}
## The END{} block is executed after all files have been processed.
## This is useful since you may have more than one line whose 3rd
## field was specified in the first file so you don't want to print
## as you process the files.
END{
    ## For each element in the n array
    for (i in n){
    ## print the element itself and then its value
    print i,":",n[i];
    }
}

Sie können das als Datei speichern, ausführbar machen und wie folgt ausführen:

$ chmod a+x foo.awk
$ ./foo.awk file1 file2
1 : 7
2 : 2
3 : 4

Oder Sie können es zu einem Einzeiler verdichten:

awk '
     (NR == FNR){n[$1] = 0; next}
     {if($3 in n){n[$3]+=$5}}
     END{for (i in n){print i,":",n[i]} }' file1 file2
terdon
quelle
9
awk '
  NR == FNR {n[$3] += $5; next}
  {print $1 ": " n[$1]}' file2 file1
Stéphane Chazelas
quelle
Es erledigt einige zusätzliche Arbeiten, indem nicht übereinstimmende Felder summiert werden.
Emmanuel
@Emmanuel, das ist immer noch eine awk-Anweisung pro Zeile von file2, was es kürzer und schneller als terdons macht
Stéphane Chazelas
geniale Lösung!
Ronald Pauffert