Команда оболочки для суммирования целых чисел, по одной в строке?
Я ищу команду, которая будет принимать в качестве входных данных несколько строк текста, каждая строка содержит одно целое число, и выводить сумму этих целых чисел.
В качестве предыстории у меня есть файл журнала, который включает измерения времени, поэтому с помощью поиска соответствующих строк и небольшого переформатирования 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-
- нет необходимости в другом коде, если нужно добавить числа с плавающей запятой
Ответ 28
Или используйте awk, а не sed: арифметический пример
Ответ 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, который позволяет избежать разрыва.