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


867

(入力として)複数行のテキストを受け入れ、各行に単一の整数を含み、これらの整数の合計を出力するコマンドを探しています。

背景として、タイミング測定を含むログファイルがあります。関連する行のgreppingと少しのsed再フォーマットによって、そのファイルにすべてのタイミングをリストできます。合計を計算したいと思います。最終的な合計を行うために、この中間出力を任意のコマンドにパイプすることができます。私はexpr以前からずっと使用してきましたが、RPNモードで実行しない限り、これで対処できるとは思えません(それでもトリッキーです)。

整数の合計を取得するにはどうすればよいですか?


2
これは私がしばらく前に尋ねた質問に非常に似ていますstackoverflow.com/questions/295781/...
アンドリュー

5
私はこの質問が本当に気に入っています。正しい(または少なくとも有効な)回答がたくさんあるという事実です。
フランシスコカネド2009年

この質問はコードゴルフの問題のように感じます。codegolf.stackexchange.com :)
Gordon Bean

回答:


1322

awkのビットはそれを行うべきですか?

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

注:awkの一部のバージョンでは、2 ^ 31(2147483647)を超えるものを追加しようとすると、奇妙な動作が発生します。詳細については、コメントを参照してください。1つの提案は、ではprintfなく使用することですprint

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

7
この部屋にはたくさんの愛があります!このような単純なスクリプトを変更して、$ 1を$ 2に変更するだけでデータの2番目の列を追加する方法が気に入っています
Paul Dixon

2
入力をストリームとして処理するため、実用的な制限はありません。したがって、X行​​のファイルを処理できる場合は、X + 1を処理できると確信できます。
ポールディクソン

4
かつて、vacationユーティリティを介して実行されるawkスクリプトを使用して、初歩的なメーリングリストプロセッサを作成しました。良い時間。:)
LS

2
これを次のように使用しました:すべてのドキュメントのページのスクリプトを数えます:ls $@ | xargs -i pdftk {} dump_data | grep NumberOfPages | awk '{s+=$2} END {print s}'
空飛ぶ羊

8
awkは32ビットの符号付き整数表現を使用するため、2147483647(つまり、2 ^ 31)より大きい数値では機能しません。awk '{s+=$1} END {printf "%.0f", s}' mydatafile代わりに使用してください。
Giancarlo Sportelli、2015

665

通常、貼り付けは複数のファイルの行をマージしますが、ファイルの個々の行を単一の行に変換するためにも使用できます。デリミタフラグを使用すると、x + xタイプの方程式をbcに渡すことができます。

paste -s -d+ infile | bc

または、標準入力から配管する場合、

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

1
非常に素晴らしい!「+」の前にスペースを置いた方が、解析がしやすくなりますが、paste&then bcを介していくつかのメモリ番号をパイプするのに非常に便利です。
マイケルH.

73
awkソリューションよりも覚えやすく入力しやすいです。また、ノートpasteダッシュを使用することができ-、ファイル名として-最初のファイルを作成することなくペーストの標準出力にパイプにコマンドの出力から、番号をあなたにできるようになります:<commands> | paste -sd+ - | bc
ジョージ・

19
1億の数値を含むファイルがあります。awkコマンドには21秒かかります。貼り付けコマンドには41秒かかります。しかし、それでも「貼り付け」を満たすのは良いことです!
Abhi、2013年

4
D:@Abhiは:興味深い:DIは、私は億と1の数字試すまでけれども、それはならしので、それはawkコマンドを把握する私の20代を取るだろうと思います
マーク・K・コーワン

6
@Georgeただし、省略でき-ます。(ファイル stdin と組み合わせる場合に便利です)。
Alois Mahdal

128

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

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

ワンライナーはsys.argvの[]内のファイルのために仕事をしませんが、その一つがない上にstackoverflow.com/questions/450799/...
JFS

確かに、作者は別のスクリプトからの出力をコマンドにパイプするつもりだと言っており、私はそれをできるだけ短くしようとしました:)
dF。

39
短いバージョンは次のようになりますpython -c"import sys; print(sum(map(int, sys.stdin)))"
jfs 2009年

4
読みやすさと柔軟性から、この答えが大好きです。私は、ディレクトリの集まりでの10Mb未満のファイルの平均サイズを必要とし、これにそれを修正:find . -name '*.epub' -exec stat -c %s '{}' \; | python -c "import sys; nums = [int(n) for n in sys.stdin if int(n) < 10000000]; print(sum(nums)/len(nums))"
ポールWhipp

1
:あなたは、いくつかのテキストが中に混入している場合は、非数字を除外することができますimport sys; print(sum(int(''.join(c for c in l if c.isdigit())) for l in sys.stdin))
Granitosaurus

91

一般的に承認されているソリューションに大きな警告を出します:

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

ここでprintf()が役立つのはなぜですか?加算コードが同じであるため、その前にintのオーバーフローが発生しています。
Robert Klemme、2015年

9
問題は実際には「印刷」機能にあるからです。Awkは64ビット整数を使用しますが、何らかの理由でprintはそれらを32ビットにスケーリングします。
Giancarlo Sportelli 2015年

4
私は誤解だ場合を除き、プリントバグは、少なくともawkの4.0.1&bashの4.3.11のために、固定されるように表示されます。echo -e "2147483647 \n 100" |awk '{s+=$1}END{print s}'ショー2147483747
Xen2050

4
フロートを使用すると、新しい問題がecho 999999999999999999 | awk '{s+=$1} END {printf "%.0f\n", s}'発生するだけです。生産1000000000000000000
Patrick

1
64ビットシステムで "%ld"を使用するだけで、printfが32ビットに切り捨てられないのではないですか?@Patrickが指摘しているように、フロートはここでは素晴らしいアイデアではありません。
yerforkferchips


66
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. 結果を出力します。スタックに残った唯一のアイテムです。

のシンプルさとパワーを本当に理解するために、上記のコマンドのPythonバージョンのdcコマンドを実装して実行する、動作するPythonスクリプトを次に示しますdc

### 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()

2
dcは、使用するのに最適なツールです。しかし、私は少し少ないスタック操作でそれを行います。すべての行に実際に数字が含まれていると仮定します:(echo "0"; sed 's/$/ +/' inp; echo 'pq')|dc
ikrabbe

5
オンラインアルゴリズム:dc -e '0 0 [+?z1<m]dsmxp'。そのため、処理する前にすべての数値をスタックに保存するのではなく、1つずつ読み取って処理します(1行に複数の数値を含めることができるため、正確には1行ずつ)。空の行は入力シーケンスを終了できることに注意してください。
ruvim

@ikrabbeいいね。実際には、もう1文字短縮sedできますdc 。引数と演算子の間のスペースは関係ないので、置換のスペースは削除できます。(echo "0"; sed 's/$/+/' inputFile; echo 'pq')|dc
WhiteHotLoveTiger 2016年

58

JQ

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

7
私はこれが好きです。はっきりと短く、実際にそれを覚えているかもしれないと思うからです。
Alfe、2017年

46

純粋で短いバッシュ。

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

9
最初の行をで置き換えてもサブプロセスは作成されないため、これが最良のソリューションですf=$(<numbers.txt)
loentar 2013年

1
stdinからの入力を取得する方法はありますか?パイプから?
njzk2 2014年

@ njzk2 f=$(cat); echo $(( ${f//$'\n'/+} ))スクリプトを入力する場合、そのスクリプトに何かをパイプするか、インタラクティブなstdin入力の引数なしでそれを呼び出すことができます(Control-Dで終了)。
mklement0 2014

5
@loentarこれ<numbers.txtは改善ですが、全体として、このソリューションは小さな入力ファイルに対してのみ効率的です。たとえば、入力行が1,000行のファイルの場合、受け入れられたawk解決策は私のマシンで約20倍速くなります。また、ファイルが一度にすべて読み取られないため、消費するメモリも少なくなります。
mklement0 2014

2
私がこれに到達したとき、私はほとんど希望を失っていました。ピュアバッシュ!
Omer Akhter、2015年

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

4
そして、それらを追加し直しました。「-l」は、出力がシェル ''バックティックとほとんどのプログラムが期待するとおりにLFで終了することを保証し、「<」は、このコマンドがパイプラインで使用できることを示します。
j_random_hacker 2009年

あなたが正しいです。言い訳として:Perlワンライナーの各文字は私にとって精神的な作業を必要とするため、できるだけ多くの文字を削除することを好みます。この場合、その習慣は有害でした。
jfs 2009年

2
すべてをRAMにロードしない数少ないソリューションの1つ。
Erik Aronesty 2016年

28

私の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

私の入力には空白行が含まれる可能性があるため、ここに投稿したものとを使用しましたgrep -v '^$'。ありがとう!
James Oravec

ワオ!!あなたの答えは素晴らしいです!すべてのトレッドの中で私の個人的なお気に入り
thahgr

これを愛し、パイプラインのために+1してください。私にとって非常にシンプルで簡単なソリューション
Gelin Luo

24

私は既存の答えで簡単なベンチマークを行いました

  • 標準ツールのみを使用します(luaまたはのようなものについては申し訳ありませんrocket)、
  • 本物のワンライナーです
  • 膨大な数(1億)を追加できる
  • 高速です(1分以上かかったものは無視しました)。

私は常に、1〜1億の数値を追加しました。これは、いくつかのソリューションで1分未満で私のマシンで実行できました。

結果は次のとおりです。

パイソン

:; 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ソリューションは約30%から11秒までのオーバーヘッドを追加するだけです。しかし、公平を期すために、Pypyは厳密には標準ではありません。ほとんどの人はCPythonをインストールしているだけで、これは非常に遅い(22秒)、人気のあるAwkソリューションとまったく同じ速さです。

標準ツールに基づく最速のソリューションはPerl(15秒)です。


2
paste+のbcアプローチは、私が和進値に探していただけで何のおかげでした!
Tomislav Nakic-Alfirevic 2017年

1
ちょうど楽しみのために、ルストで:use std::io::{self, BufRead}; fn main() { let stdin = io::stdin(); let mut sum: i64 = 0; for line in stdin.lock().lines() { sum += line.unwrap().parse::<i64>().unwrap(); } println!("{}", sum); }
ジョスリン

素晴らしい答え。ちらほらと言うわけではありませんが、実行時間の長い結果を含めることにした場合、答えはさらに素晴らしいものになります。
スティーブン・ルー

@StevenLu私は答えが(あなたの言葉を使うために)長くなり、それゆえにそれほど素晴らしくないと感じました。しかし、私はこの感情を誰もが共有する必要はないことを理解できます:)
Alfe

次:numba +並列化
gerrit

17

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

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

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

7
猫は必要ありません: echo $(( $( tr "\n" "+" < /tmp/test) 0 ))
agc

2
trは「プレーン・バッシュ」ではありません/ nitpick
Benjamin W.

17

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

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

次に、使用法:

addnums < /tmp/nums

14

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

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

このコマンドを使用するには、標準入力を介して数値リストを渡すか、数値を含むファイルをパラメーターとして渡します。


2
これは、DUPです:stackoverflow.com/questions/450799/...
JFS

11

以下はbashで動作します:

I=0

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

echo $I

1
ファイルが任意に大きくなる可能性がある場合、コマンド展開は注意して使用する必要があります。numbers.txtが10MBの場合、このcat numbers.txtステップには問題があります。
ジャコモ、

1
確かに、しかし、(ここで見つかったより良い解決策がない場合は)私は実際にその問題に遭遇するまでこれを使用します。
フランシスコカネド2009年

11

num-utilsを使用することもできますが、必要なものには過剰な場合があります。これは、シェルで数値を操作するためのプログラムのセットであり、もちろんそれらを加算するなど、いくつかの気の利いたことを行うことができます。それは少し時代遅れですが、それでも機能し、さらに何かをする必要がある場合に役立ちます。

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


例:numsum numbers.txt
agc

9

これは古い質問だと思いますが、私はこの解決策を共有できるほど気に入っています。

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

興味があれば、その仕組みを説明します。


10
しないでください。-nと-pは、単なる文字列の貼り付けではなく、意味のあるものであるかのように振る舞います;)
hobbs

2
はい、説明してください:)(私はPerlのタイプの男ではありません。)
イェンス

1
「perl -MO = Deparse -lpe '$ c + = $ _} {$ _ = $ c'」を実行して出力を確認してください。基本的に-lは改行と入力と出力の両方の区切り文字を使用し、-pは各行を出力します。しかし、 '-p'を実行するために、perlは最初にボイラープレートを追加します(これは-MO = Deparse)が表示しますが、その後、置換してコンパイルするだけです。したがって、 '} {'部分で追加ブロックを挿入し、それをだまして各行に印刷せず、最後に印刷することができます。
Nym 2013

9

純粋な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

((括弧が2つあるのはなぜ))ですか?
Atcold 2015年

猫のせいで本当に純粋なバッシュではない。猫を置き換えることによってそれ純粋なbashのを作る$(< numbers.txt)
Dani_l


6

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

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

または少し短い:perl -e 'map {$ x + = $ _} <>; print $ x 'infile.txt
Avi

1000万の数値を大量に入力する場合、必要なメモリはほぼ2GBです
Amit Naidu

6

ルビー愛好家のために

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

5

これを提出することは避けられません:

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

ここにあります:
任意の精度の数値のリストを合計する最もエレガントなUNIXシェルのワンライナー?

そして、これはawk、bc、および友人に対するその特別な利点です:

  • バッファリングに依存しないため、非常に大きな入力で窒息しない
  • 特定の精度-またはその問題の整数サイズ-制限を意味しない
  • 浮動小数点数を追加する必要がある場合、別のコードは不要

回答に質問に関連するコードを含め、リンクを参照しないでください
Ibo

5

GNU datamash utilを使用する:

seq 10 | datamash sum 1

出力:

55

入力データが不規則で、奇妙な場所にスペースとタブがある場合、これは混乱する可能性がありますdatamash。その場合は、-Wスイッチを使用します。

<commands...> | datamash -W sum 1

...またはtr空白をクリーンアップするために使用:

<commands...> | tr -d '[[:blank:]]' | datamash sum 1


3

快適であれば、Pythonでそれを行うことができます。

テストせず、入力しただけ:

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

セバスチャンはワンライナースクリプトを指摘しました:

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

python -c "from fileinput import input; print sum(map(int、input()))"
numbers.txt

2
猫は乱用されています。ファイルから標準入力をリダイレクトします:python -c "..." <numbers.txt
Giacomo

2
@rjack:catスクリプトがstdinとargv []のファイル(while(<>)Perlのように)の両方で機能することを示すために使用されます。入力がファイル内にある場合、「<」は不要です。
jfs 2009年

2
しかし< numbers.txt、標準入力でも同じように機能することを示してcat numbers.txt |います。そしてそれは悪い習慣を教えていません。
Xiong Chiamiov 2013年

3
$猫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の回答を参照してください。


3

以下は機能するはずです(あなたの番号が各行の2番目のフィールドであると仮定します)。

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

2
{sum = 0}の部分は実際には必要ありません
Uphill_ What '1

3

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

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

3

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)

私はコメントを賛成しなければなりませんでした。答えに問題はありません-それはかなり良いです。ただし、コメントがすばらしい回答であることを示すために、コメントを賛成しています。
bballdave025

3

バックティック( "` ")の読みやすさについては事前に謝罪しますが、これらはbash以外のシェルで機能するため、より貼り付け可能です。それを受け入れるシェルを使用する場合、$(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

これは、行ごとに合計するだけの場合です。ただし、データファイル内のすべての数の合計

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

ところで、デスクトップで何かをすばやく行う必要がある場合は、これを使用します。

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

2
サポートしていない、どのような古代のシェルを使用してい$()ますか?
nyuszika7h 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.