Come posso sommare rapidamente tutti i numeri in un file?


16

Ogni riga contiene testo e numeri in una colonna. Devo calcolare la somma dei numeri in ogni riga. Come posso fare ciò? Grazie

esempio.log contiene:

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

La risposta dovrebbe essere 784


Ho provato questo metodo awk '{sum + = $ 1}; END {print sum} 'example.log ma è solo per i numeri in linea
Jack

2
C'è quasi la stessa domanda in Stack Overflow : come posso sommare rapidamente tutti i numeri in un file? . Forse è tempo di avere duplicati tra siti?
fedorqui,

Risposte:


18

Se la tua opzione di grepsupporto -o, puoi provare:

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

POSIXly:

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

16

Con una versione più recente (4.x) di GNU awk:

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

Con altri awks prova:

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

4
È necessario s+0nel caso in cui ssia vuoto, verrà stampato 0anziché vuoto.
cuonglm,

Lascia che te lo spieghi. - C'è solo un caso in cui spuò essere vuoto; se i dati di input non contengono righe (ovvero se non sono presenti input ). In quel caso ci sono due comportamenti possibili; 1) nessun input => nessun output, o 2) emette sempre qualcosa, se solo 0. Entrambi sono opzioni sensibili a seconda del contesto dell'applicazione. L' +0opzione di indirizzamento 2). Per affrontare l'opzione 1) preferiresti scrivere END {if(s) print s}. - Pertanto non ha senso assumere entrambe le opzioni (per questo caso d'angolo senza dati) fino a quando non viene specificato dalla domanda.
Janis,

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

2
Preferiamo risposte lunghe. Puoi per favore approfondire come funziona?
slm

2
@slm, quella risposta non è più o meno dettagliata delle altre risposte qui ed è autoesplicativa. Ha anche il vantaggio di lavorare con input cometime=1.4e5sec
Stéphane Chazelas,

@ StéphaneChazelas - concordato, ma si tratta di un nuovo utente e incoraggiamo gli utenti a fornire più di risposte a riga singola. Un po 'di testo che spiega come funziona renderebbe una risposta molto più forte del semplice codice.
slm

4
@slm, questo è un nuovo utente con una delle migliori risposte (dal punto di vista tecnico) e ottiene due voti negativi e un commento negativo. Non è un caloroso benvenuto.
Stéphane Chazelas,

1
@TomFenech, la sintassi POSIX per awk richiede che questi elementi di modello / azione siano separati da ";" o "newline", quindi potresti trovare implementazioni awk dove fallisce senza questo ";".
Stéphane Chazelas,

7

Un altro GNU awk:

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

Uno perl:

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

Uno POSIX:

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

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

Risposta eccezionale, ma non è necessario sed:awk --field-separator = '{ sum+=$2 } END { print sum}' data.dat
user1717828,

@ user1717828: dovresti piuttosto usare il (più corto e più compatibile!) -F'='invece di--field-separator =
Olivier Dulac

@OlivierDulac, strano, il mio man awkunico dà -F fse--field-separator fs
user1717828

@ user1717828: -F'='oppure -F '='sono 2 i modi di fare -F fs(fs è "=" nel tuo caso). Ho aggiunto le virgolette singole per garantire che fs sia correttamente visto e interpretato da awk, non dalla shell (utile se la fs è ';' per esempio)
Olivier Dulac,

4

Puoi provare questo:

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

4

Tutti hanno pubblicato un post fantastico awk risposte , che mi piacciono molto.

Una variante di @cuonglm che sostituisce grepcon sed:

sed 's/[^0-9]//g' example.log | paste -sd'+' - | bc
  1. Il sed strisce tutto tranne i numeri.
  2. Il paste -sd+ -comando unisce tutte le linee come un'unica linea
  3. Il bcvaluta l'espressione

3

Dovresti usare una calcolatrice.

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

Con le tue quattro righe che stampano:

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

E più semplicemente:

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

... che stampa ...

31
223
241
784

Se la velocità è ciò che stai cercando, allora dcè ciò che desideri. Tradizionalmente era bcil compilatore - e lo è ancora per molti sistemi.


Non secondo le mie misurazioni : dipende da quanto lavoro devi fare per generare la formula
glenn jackman,

@glennjackman - le tue misurazioni non includono il dcpiù vicino possibile. Di cosa stai parlando?
Mikeserv,

A proposito, quando si confronta il vecchio equipaggio con il nuovo equipaggio - come quando si confronta perlil set di strumenti unix standard - non ha molto senso se si utilizzano strumenti GNU compilati su una toolchain GNU. Tutto il gonfiore che può influire negativamente sulle prestazioni di Perl è anche in tutti quei programmi di utilità GNU compilati da GNU. Triste ma vero. È necessario un set di strumenti reale, semplicemente costruito e semplice per giudicare con precisione la differenza. Come un set di cimeli di cimelio staticamente collegato, ad esempio, alle librerie di musl - in questo modo puoi mettere in panchina il paradigma di uno strumento / un lavoro rispetto a quello di uno strumento per dominarli tutti.
Mikeserv,

3

Attraverso python3,

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

re.findallrestituisce un elenco di stringhe, questo non funzionerà
iruvar,

@ 1_CR ya, me lo dimentico. Controllalo adesso.
Avinash Raj,

Forse sum(int(e) for e in l)è più pitonico.
cuonglm,

3

Soluzione bash pura (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.

Versione breve:

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

1
Forse anche:PS4='$((x+=${time%s*}))' time=0 x=0 sh -x <infile
mikeserv
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.