web-dev-qa-db-ja.com

1行に1つずつ整数を合計するシェルコマンド

各行に1つの整数を含む複数行のテキストを入力として受け入れ、それらの整数の合計を出力するコマンドを探しています。

ちょっとした背景として、私はタイミング測定値を含むログファイルを持っているので、関連する行をグリップすることによって、そして私はそのファイルのすべてのタイミングをリストすることができます。しかし、合計を計算したいのですが、最終的な合計を計算するためにこの中間出力をパイプ処理することができるコマンドについては、頭が空になっています。私はこれまでexprを使ってきましたが、RPNモードで動かない限りこれに対処するつもりはないと思います(そしてそれでもそれはトリッキーになるでしょう)。

何が足りないの?おそらくこれを達成するためのいくつかの方法があることを考えると、たとえ他の誰かがその仕事をする別の解決策をすでに投稿していたとしても、私はうまくいくすべてのアプローチを読んで(そして支持し)ます。

関連する質問: Unixで出力の列の合計を計算するための最短のコマンド? (クレジット @Andrew


更新 :うわー、予想どおりここにいくつかのいい答えがあります。私は間違いなくawkを一般的なコマンドラインツールとしてより深く検証する必要があるように見えます!

759
Andrzej Doyle

Awkのビットはそれをすべきですか?

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

注意:2 ^ 31(2147483647)を超えるものを追加しようとしている場合、awkの一部のバージョンではいくつかの奇妙な動作があります。より多くの背景についてはコメントを見てください。 1つの提案はprintfではなくprintを使うことです。

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

貼り付けは通常、複数のファイルの行をマージしますが、ファイルの個々の行を1行に変換するためにも使用できます。区切り文字フラグを使用すると、x + x型の式をbcに渡すことができます。

paste -s -d+ infile | bc

あるいは、stdinから配管するときは、

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

Pythonのワンライナーバージョン:

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

私は一般的に承認された解決策に大きな警告を出すでしょう:

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
76

プレーンバッシュ:

$ 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
73
Giacomo
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

擬似コードとして:

  1. "add_top_of_stack"を次のように定義します。
    1. スタックから上位2つの値を削除し、結果を元に戻す
    2. スタックに2つ以上の値がある場合は、 "add_top_of_stack"を再帰的に実行します。
  2. スタックに2つ以上の値がある場合は、 "add_top_of_stack"を実行してください。
  3. 結果を出力します。スタックに残った唯一の項目

dcの単純さと力を本当に理解するために、これはdcからのコマンドのいくつかを実装し、上記のコマンドのPython版を実行する実用的な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()
63
CB Bailey

jq :付き

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

ピュアでショートバッシュ。

f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))
45
Daniel
Perl -lne '$x += $_; END { print $x; }' < infile.txt
36
j_random_hacker

私の15セント:

$ 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
25
innocent-world

これをコマンドにしたい場合(例えば、頻繁にこれを行う必要がある場合)、BASHソリューション

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

その後の使用法:

addnums < /tmp/nums
17
Jay

既存の回答について簡単なベンチマークを行いました。

  • 標準のツールのみを使用する(luarocketのようなものは申し訳ありません)
  • 本当のワンライナーです
  • 膨大な数(1億)の数字を追加することができます。
  • 速いです(私は1分以上かかりましたものを無視しました)。

私は常に1〜1億の数を自分のマシンで1分以内にいくつかの解決策のために実行可能であると加えました。

結果は次のとおりです。

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

これは私のマシンのメモリ不足になりました。それは入力の半分のサイズ(5000万の数)でうまくいきました:

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

だから私はそれが1億の数字のために〜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;
}

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

結論

Cはもちろん8秒で最速ですが、 Pypyソリューションは11秒に30%程度のごくわずかなオーバーヘッドしか追加しません 。しかし、公平に言うと、Pypyは標準的なものではありません。ほとんどの人はCPythonをインストールしているだけで、これは非常に遅く(22秒)、人気のあるAwkのソリューションとまったく同じ速さです。

標準的なツールに基づいた最速の解決策はPerl(15秒)です。

17
Alfe

プレーンバッシュワンライナー

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

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

以下はbashで動作します。

I=0

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

echo $I
11

Num-utilsを使うこともできますが、必要以上にやり過ぎるかもしれません。これはシェル内で数値を操作するためのプログラムのセットであり、もちろんそれらを足し合わせるなど、いくつかの気の利いたことができます。それは少し古くなっていますが、それらはまだ機能していて、あなたがもっと何かをする必要があるならば役に立つかもしれません。

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

11
sykora

AWKはあなたが探しているものだと思います。

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

このコマンドは、番号リストを標準入力に渡すか、または番号を含むファイルをパラメータとして渡すことによって使用できます。

10
Paolo

私はこれが古い質問であることを認識していますが、私はそれを共有するのに十分なこの解決策が好きです。

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

興味があれば、私はそれがどのように機能するかを説明します。

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

ピュアバッシュとワンライナーで:-)

$ 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
8
Oliver Ertl

Rubyの恋人のために

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

代替の純粋なPerl、かなり読みやすい、パッケージやオプションは不要:

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

私のバージョン:

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

あなたが快適に感じるなら、あなたはPythonでそれをすることができます:

テストされていない、ただタイプされた:

out = open("filename").read();
lines = out.split('\n')
ints = map(int, lines)
s = sum(ints)
print s

Sebastianは、一筆書きのスクリプトを指摘しました。

cat filename | python -c"from fileinput import input; print sum(map(int, input()))"
3
Tiago

次のようにします(あなたの番号が各行の2番目のフィールドであると仮定します)。

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

C (not simplified)

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)
3
Greg Bowyer

これを送信することは避けられません。

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

それはここで見つけられます:
任意の精度の数のリストを合計するための最も洗練されたunix Shell one-liner?

そしてこれがawk、bc、そして友達よりも優れた特別な利点です。

  • それはバッファリングには依存せず、したがって本当に大きな入力では妨げられません。
  • それは特定の精度を意味しません - あるいはその問題に対する整数サイズ - 限界
  • 浮動小数点数を追加する必要がある場合は、別のコードは必要ありません。
3
fgeorgatos

ラケットのワンライナー:

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

C++(簡体字):

echo {1..10} | scc 'WRL n+=$0; n'

SCCプロジェクト - http://volnitsky.com/project/scc/

SCCはシェルプロンプトでのC++スニペット評価者です

2

バックティックの読みやすさ( "` ")を前もって謝罪しますが、これらはbash以外のシェルで機能するため、より貼り付けることができます。あなたがそれを受け入れるShellを使うなら、$(command ...)フォーマットは `command ...`より読みやすい(そしてデバッグ可能)ので、あなたの正気のために自由に変更してください。

私のbashrcにはawkを使って簡単な数学項目を計算する簡単な関数があります。

calc(){
  awk 'BEGIN{print '"$@"' }'
}

これは、+、 - 、*、/、^、%、sqrt、sin、cos、括弧...(そしてもっとあなたのawkのバージョンに依存する)をするでしょう...あなたはprintfとフォーマット浮動小数点で空想を得ることさえできます出力が、これは私が通常必要とするすべてです

この質問では、各行について単純にこれを行います。

calc `echo "$@"|tr " " "+"`

そのため、各行を合計するコードブロックは次のようになります。

while read LINE || [ "$LINE" ]; do
  calc `echo "$LINE"|tr " " "+"` #you may want to filter out some lines with a case statement here
done

それはあなたがそれらを1行ずつ合計したいだけの場合です。ただし、データファイル内の合計 every numberの場合

VARS=`<datafile`
calc `echo ${VARS// /+}`

ところで私はデスクトップ上で何かを迅速に行う必要がある場合は、これを使用します。

xcalc() { 
  A=`calc "$@"`
  A=`Xdialog --stdout --inputbox "Simple calculator" 0 0 $A`
  [ $A ] && xcalc $A
}
2
technosaurus

好みの 'expr'コマンドを使用して、最初に入力を少し細かくするだけで済みます。

seq 10 | tr '[\n]' '+' | sed -e 's/+/ + /g' -e's/ + $/\n/' | xargs expr

プロセスは以下のとおりです。

  • "tr"はeoln文字を+記号に置き換えます。
  • sedは両側にスペースを入れて '+'を埋め、次に行から最後の+を取り除きます。
  • xargsはexprが消費するようにパイプライン入力をコマンドラインに挿入します。
2
Alan Dyke

あなたはいくつかの数クランチタスクの進行状況を監視できるようにするためのリアルタイムの合計。

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

$ cat numbers.txt | while read new; do total=$(($total + $new)); echo $total; done
1
3
6
10
15
21
28
36
45
55

(この場合、$totalをゼロに設定する必要はありません。また、終了後に$ totalにアクセスすることもできません。)

2
sanmai
 $ 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の答え を参照してください。

2
Sinan Ünür

完全を期すために、Rソリューションもあります。

seq 1 10 | R -q -e "f <- file('stdin'); open(f); cat(sum(as.numeric(readLines(f))))"
1
drmariod

環境変数tmpを使う

tmp=awk -v tmp="$tmp" '{print $tmp" "$1}' <filename>|echo $tmp|sed "s/ /+/g"|bc

tmp=cat <filename>|awk -v tmp="$tmp" '{print $tmp" "$1}'|echo $tmp|sed "s/ /+/g"|bc

ありがとう。

1
김상헌

Alacon - Alasql database用のコマンドラインユーティリティで実行できます。

Node.jsと連携するので、 Node.js をインストールし、次に Alasql packageをインストールする必要があります。

標準入力から合計を計算するには、次のコマンドを使用します。

> cat data.txt | node alacon "SELECT VALUE SUM([0]) FROM TXT()" >b.txt
1
agershun

LuaインタプリタはすべてのFedoraベースのシステム[Fedora、RHEL、CentOS、kororaなど]に存在します。なぜならそれはrpm-package(パッケージマネージャrpmのパッケージ)、すなわちrpm-luaに埋め込まれているからです。この種の問題は理想的です(あなたもあなたの仕事を成し遂げるでしょう)。

cat filname | lua -e "sum = 0;for i in io.lines() do sum=sum+i end print(sum)"

そしてそれはうまくいきます。ただし、Luaは冗長です。キーボードの繰り返しの怪我に耐える必要があるかもしれません。

1
fedvasu

1つの簡単な解決策はあなたのためにそれをするためのプログラムを書くことでしょう。これはおそらくpythonで非常に素早くできるでしょう。

sum = 0
file = open("numbers.txt","R")
for line in file.readlines(): sum+=int(line)
file.close()
print sum

私はそのコードをテストしていませんが、それは正しく見えます。 numbers.txtをファイルの名前に変更し、そのコードをsum.pyという名前のファイルに保存します。コンソールタイプで "python sum.py"と入力します。

0
Matt Boehm
#include <iostream>

int main()
{
    double x = 0, total = 0;
    while (std::cin >> x)
        total += x;
    if (!std::cin.eof())
        return 1;
    std::cout << x << '\n';
}
0
Tony Delroy

_ gnu _ datamash util を使用します。

seq 10 | datamash sum 1

出力:

55
0
agc

Rebolのワンライナー:

rebol -q --do 's: 0 while [d: input] [s: s + to-integer d] print s' < infile.txt

残念ながら上記はRebol 3ではまだうまくいきません( _ input _ はSTDINをストリーミングしません)。

これがRebol 3でも機能する暫定的な解決策です。

rebol -q --do 's: 0 foreach n to-block read %infile.txt [s: s + n] print s'
0
draegtun

...とPHPバージョンは、完全を期すためだけのものです。

cat /file/with/numbers | php -r '$s = 0; while (true) { $e = fgets(STDIN); if (false === $e) break; $s += $e; } echo $s;'
0
Ivan Krechetov