Verwenden von awk, um die Werte einer Spalte basierend auf den Werten einer anderen Spalte zu summieren

63

Ich versuche, bestimmte Zahlen in einer Spalte mit zu summieren awk. Ich möchte nur Spalte 3 der "Schmiede" summieren, um insgesamt 212 zu erhalten. Ich kann die gesamte Spalte summieren, awkaber nicht nur die "Schmiede". Ich habe:

awk 'BEGIN {FS = "|"} ; {sum+=$3} END {print sum}' filename.txt

Auch ich benutze Kitt. Vielen Dank für jede Hilfe.

smiths|Login|2
olivert|Login|10
denniss|Payroll|100
smiths|Time|200
smiths|Logout|10
jake
quelle

Antworten:

81
awk -F '|' '$1 ~ /smiths/ {sum += $3} END {print sum}' inputfilename
  • Das -FFlag setzt das Feldtrennzeichen. Ich habe es in einfache Anführungszeichen gesetzt, weil es ein spezielles Shell-Zeichen ist.
  • Dann $1 ~ /smiths/gilt den folgenden {Codeblock} nur auf Strecken , auf denen das erste Feld die Regex übereinstimmt /smiths/.
  • Der Rest ist derselbe wie Ihr Code.

Beachten Sie, dass Sie, da Sie hier nicht wirklich einen regulären Ausdruck verwenden, nur einen bestimmten Wert, genauso einfach verwenden können:

awk -F '|' '$1 == "smiths" {sum += $3} END {print sum}' inputfilename

Damit wird die Gleichheit der Zeichenfolgen überprüft. Dies entspricht der Verwendung des regulären Ausdrucks /^smiths$/, wie in einer anderen Antwort erwähnt, bei der der ^Anker nur mit dem Anfang der Zeichenfolge (dem Anfang von Feld 1) und der $Anker nur mit dem Ende der Zeichenfolge übereinstimmen. Ich bin mir nicht sicher, wie gut Sie mit Regexen vertraut sind. Sie sind sehr leistungsfähig, aber in diesem Fall können Sie genauso einfach eine String-Gleichheitsprüfung durchführen.

Platzhalter
quelle
3
Meine Lieblingsreferenz für awk ist übrigens grymoire.com/Unix/Awk.html . Sehr hilfreiche Seite.
Wildcard
1
Vielen Dank an @Wildcard! Ich war in der Lage, eine unkomprimierte Größe bestimmter Dateien in einem großen Zip-Archiv zu aggregieren, basierend auf Ihrem Rat :) unzip -lv /appl/tmp/data.lar | grep documentlibrary | awk '{sum += $1} END {print sum/1024/1024}'
Pawel
15

Ein anderer Ansatz ist die Verwendung von awk-assoziativen Arrays. Weitere Informationen finden Sie hier . Diese Zeile erzeugt die gewünschte Ausgabe:

awk -F '|' '{a[$1] += $3} END{print a["smiths"]}' filename.txt

Als Nebeneffekt speichert das Array alle anderen Werte:

awk -F '|' '{a[$1] += $3} END{for (i in a) print i, a[i]}' filename.txt

Ausgabe:

smiths 212
denniss 100
olivert 10
Andrej
quelle
Dies ist die richtige Antwort
PoVa
5

Bisher sehr gut Sie müssen lediglich einen Selektor vor dem Block hinzufügen, um die Summe zu addieren. Hier überprüfen wir, dass das erste Argument nur "Schmiede" enthält:

awk 'BEGIN {FS = "|"} ; $1 ~ /^smiths$/ {sum+=$3} END {print sum}'

Sie können dies ein wenig verkürzen, indem Sie das Feldtrennzeichen als Option angeben. Im awkAllgemeinen ist es eine gute Idee, Variablen in der Befehlszeile zu initialisieren:

awk -F'|' '$1 ~ /^smiths$/ {sum+=$3} END {print sum}'
RobertL
quelle
0
cat filename.txt | grep smiths | awk -F '|' '{sum+=$NF} END {print sum}'
  • -F Option zur Angabe des Trennzeichens.
  • $NF steht für "letzte Spalte".
Forzagreen
quelle
1
catund grepsind hier unnötig.
Andrey
Warum braucht grep @Andrey nicht? OP möchte nur "Schmiede" -Zeilen hinzufügen. Sie müssten die awk-Anweisung ändern, richtig?
EL
1
@EL Ja, die awk-Anweisung sollte geändert werden, /smiths/{...}wenn der grep-Aufruf nicht vorhanden ist. Dies ist eine geringfügige Änderung, bietet jedoch erhebliche Vorteile: Die Anzahl der ausgeführten Prozesse wird verringert, die Fehlerkontrolle vereinfacht und der Code wird klarer.
Andrey
0

Ich persönlich würde es vorziehen, den awkAbschnitt so einfach wie möglich zu halten und so viel wie möglich ohne ihn zu tun. Die kombinierte Logik nutzt die Leistung von Unix-Pipelines nicht aus und ist daher für eng verwandte Anwendungsfälle schwerer zu verstehen, zu debuggen oder zu modifizieren.

cat filename.txt | perl -pe 's{.*|}{}g' | awk '{sum+=$1} END {print sum}'
Sridhar Sarnobat
quelle