Wie kann ich schnell alle Zahlen in einer Datei summieren?


16

Jede Zeile enthält Text und Zahlen in einer Spalte. Ich muss die Summe der Zahlen in jeder Zeile berechnen. Wie kann ich das machen? Danke

example.log enthält:

time=31sec
time=192sec
time=18sec
time=543sec

Die Antwort sollte 784 sein


Ich habe diese Methode ausprobiert awk '{sum + = $ 1}; ENDE {Drucksumme} 'example.log, aber es ist nur für Zahlen in der Zeile
Jack

2
In Stack Overflow gibt es fast dieselbe Frage : Wie kann ich schnell alle Zahlen in einer Datei summieren? . Vielleicht Zeit für standortübergreifende Duplikate?
Fedorqui

Antworten:


18

Wenn Sie grepUnterstützung -owünschen, können Sie Folgendes versuchen:

$ grep -o '[[:digit:]]*' file | paste -sd+ - | bc
784

POSIXly:

$ printf %d\\n "$(( $(tr -cs 0-9 '[\n*]' <file | paste -sd+ -) ))"
784

16

Mit einer neueren Version (4.x) von GNU awk:

awk 'BEGIN {FPAT="[0-9]+"}{s+=$1}END{print s}'

Mit anderen awks versuchen:

awk -F '[a-z=]*' '{s+=$2}END{print s}'

4
Sie benötigen s+0für den Fall, dass sleer ist, wird es 0statt leer gedruckt .
Sonntag,

Lassen Sie mich das erklären. - Es gibt nur einen Fall, in dem sleer sein kann. Wenn die Eingabedaten keine Zeilen enthalten (dh wenn überhaupt keine Eingabe erfolgt ). In diesem Fall sind zwei Verhaltensweisen möglich. 1) keine Eingabe => keine Ausgabe oder 2) immer etwas ausgeben, wenn nur 0. Beide Optionen sind je nach Anwendungskontext sinnvoll. Die +0Adressierung Option 2). Um Option 1) anzusprechen, müssten Sie lieber schreiben END {if(s) print s}. - Daher ist es nicht sinnvoll, eine der beiden Optionen (für diesen Eckfall ohne Daten) anzunehmen, bis sie in der Frage angegeben sind.
Janis

10
awk -F= '{sum+=$2};END{print sum}'

2
Wir bevorzugen lange Antworten. Können Sie bitte erläutern, wie dies funktioniert?
slm

2
@slm, diese Antwort ist nicht mehr oder weniger ausführlich als die anderen Antworten hier und selbsterklärend. Es hat auch den Vorteil, mit Eingaben wietime=1.4e5sec
Stéphane Chazelas

@ StéphaneChazelas - einverstanden, aber dies ist ein neuer Benutzer und wir empfehlen Benutzern, mehr als einzeilige Antworten bereitzustellen. Ein bisschen Text, der erklärt, wie es funktioniert, würde es zu einer viel stärkeren Antwort machen als nur Code.
slm

4
@slm, dies ist ein neuer Benutzer mit einer der besten Antworten (aus technischer Sicht) und er bekommt zwei Abwertungen und einen negativen Kommentar. Kein sehr herzliches Willkommen.
Stéphane Chazelas

1
@TomFenech, die POSIX-Syntax für awk erfordert, dass diese Muster- / Aktionselemente durch ";" oder "newline", so dass Sie möglicherweise awk-Implementierungen finden, bei denen dies fehlschlägt ";".
Stéphane Chazelas

7

Ein weiterer GNU awk:

awk -v RS='[0-9]+' '{n+=RT};END{print n}'

Eine perlEins:

perl -lne'$n+=$_ for/\d+/g}{print$n'

Ein POSIX eins:

tr -cs 0-9 '[\n*]' | grep . | paste -sd + - | bc

6
sed 's/=/ /' file | awk '{ sum+=$2 } END { print sum}'

Geniale Antwort, aber keine Notwendigkeit für sed:awk --field-separator = '{ sum+=$2 } END { print sum}' data.dat
user1717828

@ user1717828: du solltest lieber den (kürzeren und kompatibleren!) -F'='verwenden--field-separator =
Olivier Dulac

@OlivierDulac, komisch, mein man awkeinziger gibt -F fsund--field-separator fs
user1717828

@ user1717828: -F'='oder es -F '='gibt zwei Möglichkeiten, dies zu tun -F fs(in Ihrem Fall ist fs "="). Ich habe die einfachen Anführungszeichen hinzugefügt, um sicherzustellen, dass das fs von awk richtig gesehen und interpretiert wird, nicht von der Shell (nützlich, wenn das fs zum Beispiel ';' ist)
Olivier Dulac

4

Sie können dies versuchen:

awk -F"[^0-9]+" '{ sum += $2 } END { print sum+0; }' file

4

Jeder hat tolle awkAntworten gepostet , die ich sehr mag.

Eine Variation von @cuonglm ersetzt grepdurch sed:

sed 's/[^0-9]//g' example.log | paste -sd'+' - | bc
  1. Die sedStreifen alles außer den Zahlen.
  2. Der paste -sd+ -Befehl verbindet alle Zeilen zu einer einzigen Zeile
  3. Der bcwertet den Ausdruck aus

3

Sie sollten einen Taschenrechner verwenden.

{ tr = \ | xargs printf '[%s=]P%d+p' | dc; } <infile 2>/dev/null

Mit Ihren vier Zeilen, die gedruckt werden:

time=31
time=223
time=241
time=784

Und einfacher:

tr times=c '    + p' <infile |dc

... was druckt ...

31
223
241
784

Wenn Geschwindigkeit das ist, wonach Sie streben, dcist es das, was Sie wollen. Traditionell war es bcder Compiler - und ist es immer noch für viele Systeme.


Nicht nach meinen Maßen : Es hängt davon ab, wie viel Arbeit Sie für die Generierung der Formel tun müssen
Glenn Jackman

@glennjackman - Ihre Messungen umfassen nicht dcso nahe wie ich es beurteilen kann. Worüber redest du?
mikeserv

Übrigens, wenn Sie die alte Crew mit der neuen Crew vergleichen - wie zum Beispiel, wenn Sie perldas Standard-Unix-Toolset als Benchmark verwenden - ist es nicht sehr sinnvoll, GNU-Tools zu verwenden, die in einer GNU-Toolchain kompiliert wurden. Alles, was die Leistung von Perl negativ beeinflussen kann, ist auch in all diesen GNU-kompilierten GNU-Utils enthalten. Traurig aber wahr. Sie benötigen ein echtes, einfach aufgebautes, einfaches Toolset, um den Unterschied genau beurteilen zu können. Wie ein Erbstück-Werkzeugkasten, der statisch mit den Bibliotheken der Muslime verknüpft ist - auf diese Weise können Sie das Ein-Werkzeug / Ein-Job-Paradigma mit dem Ein-Werkzeug-um-sie-alle-zu-regieren-vergleichen.
mikeserv

3

Durch python3,

import re
with open(file) as f:
    m = f.read()
    l = re.findall(r'\d+', m)
    print(sum(map(int, l)))

re.findallgibt eine Liste von Zeichenketten zurück, dies wird nicht funktionieren
iruvar

@ 1_CR ya, das vergesse ich. Überprüfen Sie es jetzt.
Avinash Raj

Vielleicht sum(int(e) for e in l)ist mehr pythonisch.
Dienstag,

3

Reine Bash-Lösung (Bash 3+):

while IFS= read -r line; do                   # While it reads a line:
    if [[ "$line" =~ [0-9]+ ]]; then      # If the line contains numbers:
        ((counter+=BASH_REMATCH[0]))          # Add the current number to counter
    fi                                    # End if.
done                                  # End loop.

echo "Total number: $counter"         # Print the number.
unset counter                         # Reset counter to 0.

Kurzfassung:

while IFS= read -r l; do [[ "$l" =~ [0-9]+ ]] && ((c+=BASH_REMATCH)); done; echo $c; c=0

1
Vielleicht auch:PS4='$((x+=${time%s*}))' time=0 x=0 sh -x <infile
mikeserv
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.