Команда оболочки для суммирования целых чисел, по одному на строку? - PullRequest
765 голосов
/ 16 января 2009

Я ищу команду, которая примет в качестве входных данных несколько строк текста, каждая строка содержит одно целое число, и выведет сумму этих целых чисел.

Для справки: у меня есть файл журнала, который включает измерения времени, поэтому с помощью поиска соответствующих строк и небольшого переформатирования sed я могу перечислить все значения времени в этом файле. Тем не менее, я хотел бы вычислить итоговое значение, и у меня не осталось мыслей относительно любой команды, к которой я могу направить этот промежуточный вывод, чтобы получить окончательную сумму. Я всегда использовал expr в прошлом, но если он не работает в режиме RPN, я не думаю, что он справится с этим (и даже тогда это будет сложно).

Чего мне не хватает? Учитывая, что, вероятно, есть несколько способов достичь этого, я буду рад прочитать (и поддержать) любой подход, который работает, даже если кто-то уже опубликовал другое решение, которое выполняет эту работу.

Схожий вопрос: Кратчайшая команда для вычисления суммы столбца вывода в Unix? (кредиты @ Андрей )


Обновление : Вау, как и ожидалось, здесь есть несколько хороших ответов. Похоже, мне определенно придется провести awk более глубокую проверку в качестве инструмента командной строки в целом!

Ответы [ 42 ]

18 голосов
/ 07 ноября 2017

Я сделал быстрый тест на существующие ответы, которые

  • используйте только стандартные инструменты (извините за такие вещи, как lua или rocket),
  • являются настоящими однострочниками,
  • способны добавлять огромное количество чисел (100 миллионов), а
  • быстрые (я проигнорировал те, которые заняли больше минуты).

Я всегда прибавлял числа от 1 до 100 миллионов, что выполнилось на моей машине менее чем за минуту для нескольких решений.

Вот результаты:

Python

:; seq 100000000 | python -c 'import sys; print sum(map(int, sys.stdin))'
5000000050000000
# 30s
:; seq 100000000 | python -c 'import sys; print sum(int(s) for s in sys.stdin)'
5000000050000000
# 38s
:; seq 100000000 | python3 -c 'import sys; print(sum(int(s) for s in sys.stdin))'
5000000050000000
# 27s
:; seq 100000000 | python3 -c 'import sys; print(sum(map(int, sys.stdin)))'
5000000050000000
# 22s
:; seq 100000000 | pypy -c 'import sys; print(sum(map(int, sys.stdin)))'
5000000050000000
# 11s
:; seq 100000000 | pypy -c 'import sys; print(sum(int(s) for s in sys.stdin))'
5000000050000000
# 11s

Awk

:; seq 100000000 | awk '{s+=$1} END {print s}'
5000000050000000
# 22s

Paste & Bc

Это исчерпало память на моей машине. Это работало для половины размера ввода (50 миллионов чисел):

:; seq 50000000 | paste -s -d+ - | bc
1250000025000000
# 17s
:; seq 50000001 100000000 | paste -s -d+ - | bc
3750000025000000
# 18s

Итак, я думаю, что на 100 миллионов чисел потребовалось бы ~ 35 с.

Perl

:; seq 100000000 | perl -lne '$x += $_; END { print $x; }'
5000000050000000
# 15s
:; seq 100000000 | perl -e 'map {$x += $_} <> and print $x'
5000000050000000
# 48s

рубин

:; seq 100000000 | ruby -e "puts ARGF.map(&:to_i).inject(&:+)"
5000000050000000
# 30s

C

Просто для сравнения я скомпилировал версию C и также протестировал ее, чтобы понять, насколько медленнее решения на основе инструментов.

#include <stdio.h>
int main(int argc, char** argv) {
    long sum = 0;
    long i = 0;
    while(scanf("%ld", &i) == 1) {
        sum = sum + i;
    }
    printf("%ld\n", sum);
    return 0;
}

1043 *

:; seq 100000000 | ./a.out 
5000000050000000
# 8s

Заключение

C, конечно, быстрее всего с 8 с, но решение Pypy только добавляет очень небольшие накладные расходы - около 30% к 11 с . Но, честно говоря, Pypy не совсем стандарт. У большинства людей установлен только CPython, который значительно медленнее (22 с), точно так же быстро, как и популярное решение Awk.

Самым быстрым решением на основе стандартных инструментов является Perl (15 с).

17 голосов
/ 16 января 2009

Решение BASH, если вы хотите сделать это командой (например, если вам нужно делать это часто):

addnums () {
  local total=0
  while read val; do
    (( total += val ))
  done
  echo $total
}

Тогда использование:

addnums < /tmp/nums
16 голосов
/ 11 октября 2011

однотонный лайнер

$ cat > /tmp/test
1 
2 
3 
4 
5
^D

$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))
11 голосов
/ 16 января 2009

Следующие работы в bash:

I=0

for N in `cat numbers.txt`
do
    I=`expr $I + $N`
done

echo $I
11 голосов
/ 16 января 2009

Вы можете использовать num-utils, хотя это может быть излишним для того, что вам нужно. Это набор программ для манипулирования числами в оболочке, и он может делать несколько изящных вещей, включая, конечно, их сложение. Это немного устарело, но они все еще работают и могут быть полезны, если вам нужно сделать что-то большее.

http://suso.suso.org/programs/num-utils/

10 голосов
/ 16 января 2009

Я думаю, что AWK - это то, что вы ищете:

awk '{sum+=$1}END{print sum}'

Эту команду можно использовать либо путем передачи списка номеров через стандартный ввод, либо путем передачи файла, содержащего номера, в качестве параметра.

9 голосов
/ 15 октября 2009

Я понимаю, что это старый вопрос, но мне достаточно этого решения, чтобы поделиться им.

% cat > numbers.txt
1 
2 
3 
4 
5
^D
% cat numbers.txt | perl -lpe '$c+=$_}{$_=$c'
15

Если есть интерес, я объясню, как это работает.

9 голосов
/ 17 января 2009
sed 's/^/.+/' infile | bc | tail -1
8 голосов
/ 07 октября 2012

Чистый баш и в однострочнике: -)

$ cat numbers.txt
1
2
3
4
5
6
7
8
9
10


$ I=0; for N in $(cat numbers.txt); do I=$(($I + $N)); done; echo $I
55
6 голосов
/ 08 января 2015

Альтернативный чистый Perl, достаточно читаемый, никаких пакетов или опций не требуется:

perl -e "map {$x += $_} <> and print $x" < infile.txt
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...