Команда оболочки для суммирования целых чисел, по одной в строке?

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

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

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

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


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

Ответ 1

Бит awk должен это сделать?

awk '{s+=$1} END {print s}' mydatafile

Примечание: некоторые версии awk имеют какое-то нечетное поведение, если вы собираетесь добавлять что-либо, превышающее 2 ^ 31 (2147483647). См. Комментарии для получения дополнительной информации. Одно из предложений заключается в использовании printf, а не print:

awk '{s+=$1} END {printf "%.0f", s}' mydatafile

Ответ 2

Вставка обычно объединяет строки из нескольких файлов, но также может использоваться для преобразования отдельных строк файла в одну строку. Флаг-разделитель позволяет передать уравнение типа x + x в bc.

paste -s -d+ infile | bc

В качестве альтернативы, когда трубопровод из stdin,

<commands> | paste -s -d+ - | bc

Ответ 3

Версия с одним слоем в Python:

$ python -c "import sys; print(sum(int(l) for l in sys.stdin))"

Ответ 4

Я бы поставил большое ПРЕДУПРЕЖДЕНИЕ по общепринятому решению:

awk '{s+=$1} END {print s}' mydatafile # DO NOT USE THIS!!

это потому, что в этой форме awk использует 32-разрядное целочисленное представление со знаком: оно будет переполнено для сумм, которые превышают 2147483647 (т.е. 2 ^ 31).

Более общий ответ (для суммирования целых чисел) будет:

awk '{s+=$1} END {printf "%.0f\n", s}' mydatafile # USE THIS INSTEAD

Ответ 5

Обычная bash:

$ cat numbers.txt 
1
2
3
4
5
6
7
8
9
10
$ sum=0; while read num; do ((sum += num)); done < numbers.txt; echo $sum
55

Ответ 6

dc -f infile -e '[+z1<r]srz1<rp'

Обратите внимание, что отрицательные числа с префиксом минуса должны быть переведены для dc, поскольку для этого используется префикс _, а не префикс -. Например, через tr '-' '_' | dc -f- -e '...'.

Изменить: поскольку этот ответ получил так много голосов "за безвестность", вот подробное объяснение:

Выражение [+z1<r]srz1<rp делает следующее:

[   interpret everything to the next ] as a string
  +   push two values off the stack, add them and push the result
  z   push the current stack depth
  1   push one
  <r  pop two values and execute register r if the original top-of-stack (1)
      is smaller
]   end of the string, will push the whole thing to the stack
sr  pop a value (the string above) and store it in register r
z   push the current stack depth again
1   push 1
<r  pop two values and execute register r if the original top-of-stack (1)
    is smaller
p   print the current top-of-stack

Как псевдокод:

  • Определите "add_top_of_stack" как:
    • Удалите два верхних значения из стека и добавьте результат назад
    • Если стек имеет два или более значения, запустите "add_top_of_stack" рекурсивно
  • Если стек имеет два или более значений, запустите "add_top_of_stack"
  • Распечатайте результат, теперь осталось только один элемент в стеке

Чтобы действительно понять простоту и мощность dc, вот рабочий Python script, который реализует некоторые команды из dc и выполняет версию Python указанной выше команды:

### Implement some commands from dc
registers = {'r': None}
stack = []
def add():
    stack.append(stack.pop() + stack.pop())
def z():
    stack.append(len(stack))
def less(reg):
    if stack.pop() < stack.pop():
        registers[reg]()
def store(reg):
    registers[reg] = stack.pop()
def p():
    print stack[-1]

### Python version of the dc command above

# The equivalent to -f: read a file and push every line to the stack
import fileinput
for line in fileinput.input():
    stack.append(int(line.strip()))

def cmd():
    add()
    z()
    stack.append(1)
    less('r')

stack.append(cmd)
store('r')
z()
stack.append(1)
less('r')
p()

Ответ 7

С jq:

seq 10 | jq -s 'add' # 'add' is equivalent to 'reduce .[] as $item (0; . + $item)'

Ответ 8

Чистый и короткий bash.

f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))

Ответ 9

perl -lne '$x += $_; END { print $x; }' < infile.txt

Ответ 10

Мои пятнадцать центов:

$ cat file.txt | xargs  | sed -e 's/\ /+/g' | bc

Пример:

$ cat text
1
2
3
3
4
5
6
78
9
0
1
2
3
4
576
7
4444
$ cat text | xargs  | sed -e 's/\ /+/g' | bc 
5148

Ответ 11

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

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

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

addnums < /tmp/nums

Ответ 12

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

  • используйте только стандартные инструменты (извините за такие вещи, как 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

Вставить и 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 и протестировал это также, просто чтобы понять, насколько медленнее решения на основе инструментов.

#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;
}

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

Заключение

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

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

Ответ 13

Обычный bash один вкладыш

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

$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))

Ответ 14

В bash работает следующее:

I=0

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

echo $I

Ответ 15

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

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

Ответ 16

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

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

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

Ответ 17

sed 's/^/.+/' infile | bc | tail -1

Ответ 18

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

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

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

Ответ 19

Pure bash и в одном слое: -)

$ 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

Ответ 20

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

perl -e "map {$x += $_} <> and print $x" < infile.txt

Ответ 21

Для любителей Ruby

ruby -e "puts ARGF.map(&:to_i).inject(&:+)" numbers.txt

Ответ 22

Моя версия:

seq -5 10 | xargs printf "- - %s" | xargs  | bc

Ответ 23

Вы можете сделать это в python, если вам комфортно:

Не проверено, просто набрано:

out = open("filename").read();
lines = out.split('\n')
ints = map(int, lines)
s = sum(ints)
print s
Себастьян указал на один лайнер script:
cat filename | python -c"from fileinput import input; print sum(map(int, input()))"

Ответ 24

Следующее должно работать (если ваш номер является вторым полем в каждой строке).

awk 'BEGIN {sum=0} \
 {sum=sum + $2} \
END {print "tot:", sum}' Yourinputfile.txt

Ответ 25

Один вкладыш в Racket:

racket -e '(define (g) (define i (read)) (if (eof-object? i) empty (cons i (g)))) (foldr + 0 (g))' < numlist.txt

Ответ 26

C (не упрощенный)

seq 1 10 | tcc -run <(cat << EOF
#include <stdio.h>
int main(int argc, char** argv) {
    int sum = 0;
    int i = 0;
    while(scanf("%d", &i) == 1) {
        sum = sum + i;
    }
    printf("%d\n", sum);
    return 0;
}
EOF)

Ответ 27

Невозможно избежать отправки этого:

jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc

Это найдено здесь:
Самая элегантная оболочка unix с одним строком для суммирования списка чисел произвольной точности?

И вот его особые преимущества перед awk, bc и friends:

  • он не зависит от буферизации и поэтому не задыхается от действительно больших входов
  • он не подразумевает какой-либо конкретной точности -or целого размера для этих ограничений matter-
  • нет необходимости в другом коде, если нужно добавить числа с плавающей запятой

Ответ 29

AWK уже упоминалось, поэтому я хотел бы предложить вам использовать этот язык вместо GREP и SED для сканирования исходного файла журнала. Подходящий AWK script может легко выполнить работу обоих и рассчитать интересное значение, как уже отмечали Павел и Альф.

Ответ 30

$ cat n
2
4
2
7
8
9
$ perl -MList::Util -le 'print List::Util::sum(<>)' < n
32

Или вы можете ввести числа в командной строке:

$ perl -MList::Util -le 'print List::Util::sum(<>)'
1
3
5
^D
9

Тем не менее, этот файл дублирует файл, поэтому использовать его на больших файлах не рекомендуется. См. j_random_hacker answer, который позволяет избежать разрыва.