Jak mogę szybko zsumować wszystkie liczby w pliku?

16

Każdy wiersz zawiera tekst i liczby w jednej kolumnie. Muszę obliczyć sumę liczb w każdym rzędzie. Jak mogę to zrobić? Dzięki

example.log zawiera:

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

Odpowiedź powinna wynosić 784

Jacek
źródło
Wypróbowałem tę metodę awk '{sum + = $ 1}; END {print sum} 'example.log, ale dotyczy tylko liczb w kolejce
Jack
2
W przepełnieniu stosu jest prawie to samo pytanie : jak szybko zsumować wszystkie liczby w pliku? . Może czas mieć duplikaty z różnych witryn?
fedorqui

Odpowiedzi:

18

Jeśli masz opcję grepwsparcia -o, możesz spróbować:

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

POSIXly:

$ printf %d\\n "$(( $(tr -cs 0-9 '[\n*]' <file | paste -sd+ -) ))"
784
Cuonglm
źródło
16

W nowszej wersji (4.x) GNU awk:

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

Z innymi awks spróbuj:

awk -F '[a-z=]*' '{s+=$2}END{print s}'
Janis
źródło
4
Potrzebujesz s+0w przypadku, gdy sjest pusty, wydrukuje 0zamiast pustego.
cuonglm
Pozwól mi to wyjaśnić. - Jest tylko jeden przypadek, w którym smoże być pusty; jeśli dane wejściowe nie zawierają wierszy (tj. jeśli w ogóle nie ma danych wejściowych ). W takim przypadku możliwe są dwa zachowania; 1) brak danych wejściowych => brak danych wyjściowych lub 2) zawsze coś wyjściowego, jeśli tylko 0. Oba są sensownymi opcjami w zależności od kontekstu aplikacji. Jest +0to opcja adresowania 2). Aby rozwiązać opcję 1), wolisz pisać END {if(s) print s}. - Dlatego nie ma sensu zakładać żadnej z opcji (w tym przypadku narożnym braku danych), dopóki nie zostanie to określone w pytaniu.
Janis
10
awk -F= '{sum+=$2};END{print sum}'
snth
źródło
2
Wolimy długie odpowiedzi. Czy możesz wyjaśnić, jak to działa?
slm
2
@slm, ta odpowiedź nie jest mniej więcej bardziej szczegółowa niż inne odpowiedzi tutaj i jest oczywista. Ma również tę zaletę, że pracuje z takimi wejściami jaktime=1.4e5sec
Stéphane Chazelas
@ StéphaneChazelas - zgadzam się, ale jest to nowy użytkownik i zachęcamy użytkowników do udzielania więcej niż odpowiedzi w jednym wierszu. Trochę tekstu wyjaśniającego, jak to działa, uczyniłoby to znacznie silniejszą odpowiedź niż tylko kod.
slm
4
@slm, jest to nowy użytkownik z jedną z najlepszych odpowiedzi (z technicznego punktu widzenia), a on otrzymuje dwie opinie negatywne i negatywny komentarz. Niezbyt ciepłe powitanie.
Stéphane Chazelas
1
@TomFenech, składnia POSIX dla awk wymaga, aby te elementy wzorca / akcji były oddzielone przez „;” lub „nowa linia”, więc możesz znaleźć implementacje awk, w których bez tego się nie powiedzie „;”.
Stéphane Chazelas
7

Kolejny GNU awk:

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

perlJeden:

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

POSIX jeden:

tr -cs 0-9 '[\n*]' | grep . | paste -sd + - | bc
Stéphane Chazelas
źródło
6
sed 's/=/ /' file | awk '{ sum+=$2 } END { print sum}'
użytkownik2570505
źródło
Niesamowita odpowiedź, ale nie ma potrzeby sed:awk --field-separator = '{ sum+=$2 } END { print sum}' data.dat
user1717828
@ user1717828: powinieneś raczej użyć (krótszego i bardziej zgodnego!) -F'='zamiast--field-separator =
Olivier Dulac
@OlivierDulac, dziwne, mój man awkjedyny daje -F fsi--field-separator fs
użytkownik1717828
@ user1717828: -F'='lub -F '='są na 2 sposoby -F fs(w twoim przypadku fs to „=”). Dodałem pojedyncze cytaty, aby upewnić się, że fs jest poprawnie widziane i interpretowane przez awk, a nie powłokę (przydatne, jeśli fs to ';' na przykład)
Olivier Dulac
4

Możesz spróbować:

awk -F"[^0-9]+" '{ sum += $2 } END { print sum+0; }' file
taliezin
źródło
4

Wszyscy opublikowali niesamowite awkodpowiedzi, które bardzo mi się podobają.

Odmiana do @cuonglm wymianie grepz sed:

sed 's/[^0-9]//g' example.log | paste -sd'+' - | bc
  1. The sedPaski wszystko z wyjątkiem numerów.
  2. The paste -sd+ -Komenda łączy wszystkie linie razem w jednej linii
  3. bcOblicza wyrażenie
Stephen Quan
źródło
3

Powinieneś użyć kalkulatora.

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

Z czterema drukowanymi liniami:

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

I prościej:

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

... które drukuje ...

31
223
241
784

Jeśli szukasz prędkości, to właśnie dctego chcesz. Tradycyjnie był bcto kompilator - i wciąż jest dostępny dla wielu systemów.

mikeserv
źródło
Nie według moich pomiarów : to zależy, ile pracy musisz zrobić, aby wygenerować formułę
glenn jackman
@glennjackman - twoje pomiary nie obejmują dctak blisko, jak mogę powiedzieć. O czym mówisz?
mikeserv
Nawiasem mówiąc, przy porównywaniu starej ekipy z nową ekipą - na przykład podczas porównywania perlze standardowym zestawem narzędzi unix - naprawdę nie ma sensu, jeśli używasz narzędzi GNU skompilowanych w łańcuchu narzędzi GNU. Wszystkie wzdęcia, które mogą negatywnie wpłynąć na wydajność Perla, występują również we wszystkich narzędziach GNU skompilowanych przez GNU. Smutne ale prawdziwe. Potrzebujesz prawdziwego, prosto zbudowanego, prostego zestawu narzędzi, aby dokładnie ocenić różnicę. Jak na przykład zestaw narzędzi rodowych, statycznie powiązany z bibliotekami muzułmańskimi - w ten sposób możesz porównać paradygmat jedno narzędzie / jedno zadanie z jednym narzędziem, aby rządzić nimi wszystkimi.
mikeserv
3

Poprzez python3,

import re
with open(file) as f:
    m = f.read()
    l = re.findall(r'\d+', m)
    print(sum(map(int, l)))
Avinash Raj
źródło
re.findallzwraca listę ciągów, to nie zadziała
iruvar
@ 1_CR tak, zapomniałem o tym. Sprawdź to teraz.
Avinash Raj
Może sum(int(e) for e in l)jest bardziej pythonowy.
cuonglm
3

Rozwiązanie Pure Bash (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.

Krótka wersja:

while IFS= read -r l; do [[ "$l" =~ [0-9]+ ]] && ((c+=BASH_REMATCH)); done; echo $c; c=0
Helio
źródło
1
Może także:PS4='$((x+=${time%s*}))' time=0 x=0 sh -x <infile
mikeserv