Como posso somar rapidamente todos os números em um arquivo?

16

Cada linha contém texto e números em uma coluna. Eu preciso calcular a soma dos números em cada linha. Como eu posso fazer isso? Valeu

example.log contém:

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

A resposta deve ser 784

Jack
fonte
Eu tentei esse método awk '{sum + = $ 1}; END {print sum} 'example.log, mas é apenas para números em linha
Jack
2
Há quase a mesma pergunta no Stack Overflow : Como posso somar rapidamente todos os números em um arquivo? . Talvez seja hora de ter duplicatas entre sites?
Fedorqui # 28/15

Respostas:

18

Se sua opção de grepsuporte -o, você pode tentar:

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

POSIXly:

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

Com uma versão mais recente (4.x) do GNU awk:

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

Com outras awktentativas:

awk -F '[a-z=]*' '{s+=$2}END{print s}'
Janis
fonte
4
Você precisa s+0, caso sesteja vazio, ele será impresso em 0vez de vazio.
Cuonglm
Deixe-me explicar isso. - Há apenas um caso em que spode estar vazio; se os dados de entrada não contiverem linhas (ou seja, se não houver nenhuma entrada ). Nesse caso, existem dois comportamentos possíveis; 1) sem entrada => sem saída, ou 2) sempre produz algo, se apenas 0. Ambos são opções sensíveis, dependendo do contexto da aplicação. A +0opção de endereçamento 2). Para abordar a opção 1), você prefere escrever END {if(s) print s}. - Portanto, não faz sentido assumir uma das opções (neste caso de canto sem dados) até que seja especificada pela pergunta.
Janis
10
awk -F= '{sum+=$2};END{print sum}'
snth
fonte
2
Preferimos respostas longas. Você pode elaborar como isso funciona?
slm
2
@ SLM, essa resposta não é mais ou menos detalhada do que as outras respostas aqui e é auto-explicativa. Ele também tem a vantagem de trabalhar com entradas comotime=1.4e5sec
Stéphane Chazelas 28/05
@ StéphaneChazelas - concordou, mas este é um novo usuário e nós encorajamos os usuários a fornecer mais do que respostas de linha única. Um pouco de texto explicando como funciona tornaria uma resposta muito mais forte do que apenas código.
Slm
4
@ slm, este é um novo usuário com uma das melhores respostas (do ponto de vista técnico) e ele recebe dois votos negativos e um comentário negativo. Não é uma recepção calorosa.
Stéphane Chazelas
11
@ TomFenech, a sintaxe do POSIX para awk requer que esses itens de padrão / ação sejam separados por ";" ou "nova linha", para que você possa encontrar implementações awk onde ela falha sem isso ";".
Stéphane Chazelas
7

Outro GNU awk:

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

Um perl:

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

POSIX:

tr -cs 0-9 '[\n*]' | grep . | paste -sd + - | bc
Stéphane Chazelas
fonte
6
sed 's/=/ /' file | awk '{ sum+=$2 } END { print sum}'
user2570505
fonte
Resposta impressionante, mas não há necessidade de sed:awk --field-separator = '{ sum+=$2 } END { print sum}' data.dat
user1717828
@ user1717828: você deveria usar o (mais curto e mais compatível!) em -F'='vez de #--field-separator =
Olivier Dulac
@OlivierDulac, estranho, meu man awkúnico dá -F fse--field-separator fs
user1717828
@ user1717828: -F'='ou -F '='existem duas maneiras de fazer isso -F fs(fs é "=" no seu caso). Eu adicionei os singlequotes para garantir que os fs está devidamente visto e interpretado por awk, não o shell (útil se os fs é ';', por exemplo)
Olivier Dulac
4

Você pode tentar isso:

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

Todo mundo postou awkrespostas incríveis , das quais eu gosto muito.

Uma variação para @cuonglm substituindo greppor sed:

sed 's/[^0-9]//g' example.log | paste -sd'+' - | bc
  1. o sed tiras tudo, exceto para os números.
  2. o paste -sd+ - comando une todas as linhas como uma única linha
  3. A bcavalia a expressão
Stephen Quan
fonte
3

Você deve usar uma calculadora.

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

Com suas quatro linhas que imprimem:

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

E mais simplesmente:

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

... que imprime ...

31
223
241
784

Se a velocidade é o que você procura, dcé o que deseja. Tradicionalmente, era bco compilador - e ainda é para muitos sistemas.

mikeserv
fonte
Não de acordo com minhas medidas : depende de quanto trabalho você tem que fazer para gerar a fórmula
glenn jackman
@glennjackman - suas medidas não incluem o dcmais próximo que eu posso dizer. Do que você está falando?
mikeserv
A propósito, ao comparar a equipe antiga com a nova equipe - como quando você faz um benchmark perldo conjunto de ferramentas padrão do unix - realmente não faz muito sentido se você usar as ferramentas GNU compiladas em uma cadeia de ferramentas GNU. Todo o inchaço que pode afetar negativamente o desempenho do Perl também está em todos os utilitários GNU compilados pelo GNU. Triste mas verdadeiro. Você precisa de um conjunto de ferramentas simples, simples e simples, para avaliar com precisão a diferença. Como um conjunto de ferramentas de herança ligado estaticamente a bibliotecas muçulmanas, por exemplo - dessa maneira, você pode comparar o paradigma de uma ferramenta / uma tarefa versus o de uma ferramenta para governar todos eles.
mikeserv
3

Através do python3,

import re
with open(file) as f:
    m = f.read()
    l = re.findall(r'\d+', m)
    print(sum(map(int, l)))
Avinash Raj
fonte
re.findallretorna uma lista de strings, isso não vai funcionar
Iruvar
@ 1_CR ya, eu esqueço isso. Verifique agora.
Avinash Raj
Talvez sum(int(e) for e in l)seja mais pitônico.
Cuonglm
3

Solução de 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.

Versão curta:

while IFS= read -r l; do [[ "$l" =~ [0-9]+ ]] && ((c+=BASH_REMATCH)); done; echo $c; c=0
Helio
fonte
11
Talvez também:PS4='$((x+=${time%s*}))' time=0 x=0 sh -x <infile
mikeserv