Как я могу быстро сложить все числа в файле?

16

Каждая строка содержит текст и цифры в одном столбце. Мне нужно рассчитать сумму чисел в каждой строке. Как я могу это сделать? Спасибо

example.log содержит:

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

Ответ должен быть 784

разъем
источник
Я попробовал этот метод awk '{sum + = $ 1}; END {print sum} 'example.log, но это только для чисел в строке
Джек,
2
В переполнении стека почти такой же вопрос : как быстро сложить все числа в файле? , Может быть, пора иметь дубликаты между сайтами?
Федорки

Ответы:

18

Если ваш вариант grepподдержки -o, вы можете попробовать:

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

POSIXly:

$ printf %d\\n "$(( $(tr -cs 0-9 '[\n*]' <file | paste -sd+ -) ))"
784
cuonglm
источник
16

С более новой версией (4.x) GNU awk:

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

С другими awkпопробуйте:

awk -F '[a-z=]*' '{s+=$2}END{print s}'
Janis
источник
4
Вам нужно s+0в случае, когда sпусто, он будет печатать 0вместо пустого.
cuonglm
Позвольте мне объяснить это. - есть только один случай, когда sможет быть пусто; если входные данные не содержат строк (т. е. если вообще нет входных данных ). В этом случае возможны два поведения; 1) нет ввода => нет вывода или 2) всегда что-то выводит, если только 0. Оба являются разумными вариантами в зависимости от контекста приложения. +0Обращается вариант 2). Для адреса вариант 1) вам лучше написать END {if(s) print s}. - Поэтому нет смысла принимать любой вариант (для этого углового случая без данных), пока он не будет определен вопросом.
Янис
10
awk -F= '{sum+=$2};END{print sum}'
snth
источник
2
Мы предпочитаем длинные ответы. Можете ли вы рассказать, как это работает?
SLM
2
@slm, этот ответ не более или менее многословен, чем другие ответы здесь, и говорит сам за себя. У этого также есть преимущество работы с вводом какtime=1.4e5sec
Стефан Шазелас
@ StéphaneChazelas - согласен, но это новый пользователь, и мы призываем пользователей предоставлять более чем одну строку ответов. Немного текста, объясняющего, как это работает, сделало бы его намного более сильным ответом, чем просто код.
SLM
4
@slm, это новый пользователь с одним из лучших ответов (с технической точки зрения), и он получает два отрицательных отзыва и отрицательный комментарий. Не очень теплый прием.
Стефан Шазелас
1
@TomFenech, синтаксис POSIX для awk требует, чтобы эти элементы шаблона / действия были разделены либо ";" или "новая строка", так что вы можете найти реализации awk, где это не сработает без этого ";".
Стефан Шазелас
7

Еще один GNU awkодин:

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

Один perl:

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

POSIX один:

tr -cs 0-9 '[\n*]' | grep . | paste -sd + - | bc
Стефан Шазелас
источник
6
sed 's/=/ /' file | awk '{ sum+=$2 } END { print sum}'
user2570505
источник
Потрясающий ответ, но не нужно sed:awk --field-separator = '{ sum+=$2 } END { print sum}' data.dat
user1717828
@ user1717828: вам лучше использовать (короче и более совместимо!) -F'='вместо--field-separator =
Оливье Дюлак
@OlivierDulac, странно, мой man awkединственный дает -F fsи--field-separator fs
user1717828
@ user1717828: -F'='или -F '='есть 2 способа сделать -F fs(fs это "=" в вашем случае). Я добавил одинарные кавычки, чтобы гарантировать, что fs правильно виден и интерпретируется awk, а не оболочкой (например, полезно, если fs равен ';')
Оливье Дюлак
4

Вы можете попробовать это:

awk -F"[^0-9]+" '{ sum += $2 } END { print sum+0; }' file
taliezin
источник
4

Все опубликовали отличные awkответы, которые мне очень нравятся.

Вариант @cuonglm grepс заменой на sed:

sed 's/[^0-9]//g' example.log | paste -sd'+' - | bc
  1. sedПолосы все для чисел , за исключением.
  2. Команда paste -sd+ -объединяет все строки в одну строку
  3. bcВычисляет выражение
Стивен Куан
источник
3

Вы должны использовать калькулятор.

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

С вашими четырьмя линиями, которые печатают:

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

И проще:

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

... который печатает ...

31
223
241
784

Если dcвам нужна скорость, то вы хотите. Традиционно это был bcкомпилятор - и до сих пор для многих систем.

mikeserv
источник
Не в соответствии с моими измерениями : это зависит от того, сколько работы вам нужно сделать, чтобы создать формулу
Гленн Джекман
@glennjackman - ваши измерения не включают, dcнасколько я могу судить. О чем ты говоришь?
mikeserv
Кстати, при сравнении старой команды с новой командой - например, когда вы проводите тестирование по сравнению perlсо стандартным набором инструментов Unix - это не имеет особого смысла, если вы используете инструменты GNU, скомпилированные в цепочке инструментов GNU. Все навороты, которые могут негативно повлиять на производительность Perl, также присутствуют во всех этих GNU-скомпилированных утилитах GNU. Грустно, но правда. Вам нужен настоящий, просто построенный, простой набор инструментов, чтобы точно оценить разницу. Например, как набор инструментов семейной реликвии, статически связанный с мусульманскими библиотеками - таким образом, вы можете сравнить парадигму «один инструмент / одна работа» и «один инструмент, чтобы управлять ими всеми».
mikeserv
3

Через python3,

import re
with open(file) as f:
    m = f.read()
    l = re.findall(r'\d+', m)
    print(sum(map(int, l)))
Авинаш Радж
источник
re.findallвозвращает список строк, это не сработает
iruvar
@ 1_CR да, я забыл это. Проверь это сейчас.
Авинаш Радж
Может быть, sum(int(e) for e in l)это более питонический.
cuonglm
3

Чистый раствор 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.

Укороченная версия:

while IFS= read -r l; do [[ "$l" =~ [0-9]+ ]] && ((c+=BASH_REMATCH)); done; echo $c; c=0
гелиограф
источник
1
Возможно также:PS4='$((x+=${time%s*}))' time=0 x=0 sh -x <infile
mikeserv