各行に1つの整数を含む複数行のテキストを入力として受け入れ、それらの整数の合計を出力するコマンドを探しています。
ちょっとした背景として、私はタイミング測定値を含むログファイルを持っているので、関連する行をグリップすることによって、そして私はそのファイルのすべてのタイミングをリストすることができます。しかし、合計を計算したいのですが、最終的な合計を計算するためにこの中間出力をパイプ処理することができるコマンドについては、頭が空になっています。私はこれまでexpr
を使ってきましたが、RPNモードで動かない限りこれに対処するつもりはないと思います(そしてそれでもそれはトリッキーになるでしょう)。
何が足りないの?おそらくこれを達成するためのいくつかの方法があることを考えると、たとえ他の誰かがその仕事をする別の解決策をすでに投稿していたとしても、私はうまくいくすべてのアプローチを読んで(そして支持し)ます。
関連する質問: Unixで出力の列の合計を計算するための最短のコマンド? (クレジット @Andrew )
更新 :うわー、予想どおりここにいくつかのいい答えがあります。私は間違いなくawk
を一般的なコマンドラインツールとしてより深く検証する必要があるように見えます!
Awkのビットはそれをすべきですか?
awk '{s+=$1} END {print s}' mydatafile
注意:2 ^ 31(2147483647)を超えるものを追加しようとしている場合、awkの一部のバージョンではいくつかの奇妙な動作があります。より多くの背景についてはコメントを見てください。 1つの提案はprintf
ではなくprint
を使うことです。
awk '{s+=$1} END {printf "%.0f", s}' mydatafile
貼り付けは通常、複数のファイルの行をマージしますが、ファイルの個々の行を1行に変換するためにも使用できます。区切り文字フラグを使用すると、x + x型の式をbcに渡すことができます。
paste -s -d+ infile | bc
あるいは、stdinから配管するときは、
<commands> | paste -s -d+ - | bc
Pythonのワンライナーバージョン:
$ python -c "import sys; print(sum(int(l) for l in sys.stdin))"
私は一般的に承認された解決策に大きな警告を出すでしょう:
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
プレーンバッシュ:
$ 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
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
擬似コードとして:
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()
jq :付き
seq 10 | jq -s 'add' # 'add' is equivalent to 'reduce .[] as $item (0; . + $item)'
ピュアでショートバッシュ。
f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))
Perl -lne '$x += $_; END { print $x; }' < infile.txt
私の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
これをコマンドにしたい場合(例えば、頻繁にこれを行う必要がある場合)、BASHソリューション
addnums () {
local total=0
while read val; do
(( total += val ))
done
echo $total
}
その後の使用法:
addnums < /tmp/nums
既存の回答について簡単なベンチマークを行いました。
lua
やrocket
のようなものは申し訳ありません)私は常に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
:; seq 100000000 | awk '{s+=$1} END {print s}'
5000000050000000
# 22s
これは私のマシンのメモリ不足になりました。それは入力の半分のサイズ(5000万の数)でうまくいきました:
:; seq 50000000 | paste -s -d+ - | bc
1250000025000000
# 17s
:; seq 50000001 100000000 | paste -s -d+ - | bc
3750000025000000
# 18s
だから私はそれが1億の数字のために〜35秒かかっただろうと思います。
:; 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はもちろん8秒で最速ですが、 Pypyソリューションは11秒に30%程度のごくわずかなオーバーヘッドしか追加しません 。しかし、公平に言うと、Pypyは標準的なものではありません。ほとんどの人はCPythonをインストールしているだけで、これは非常に遅く(22秒)、人気のあるAwkのソリューションとまったく同じ速さです。
標準的なツールに基づいた最速の解決策はPerl(15秒)です。
プレーンバッシュワンライナー
$ cat > /tmp/test
1
2
3
4
5
^D
$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))
以下はbashで動作します。
I=0
for N in `cat numbers.txt`
do
I=`expr $I + $N`
done
echo $I
Num-utilsを使うこともできますが、必要以上にやり過ぎるかもしれません。これはシェル内で数値を操作するためのプログラムのセットであり、もちろんそれらを足し合わせるなど、いくつかの気の利いたことができます。それは少し古くなっていますが、それらはまだ機能していて、あなたがもっと何かをする必要があるならば役に立つかもしれません。
AWKはあなたが探しているものだと思います。
awk '{sum+=$1}END{print sum}'
このコマンドは、番号リストを標準入力に渡すか、または番号を含むファイルをパラメータとして渡すことによって使用できます。
私はこれが古い質問であることを認識していますが、私はそれを共有するのに十分なこの解決策が好きです。
% cat > numbers.txt
1
2
3
4
5
^D
% cat numbers.txt | Perl -lpe '$c+=$_}{$_=$c'
15
興味があれば、私はそれがどのように機能するかを説明します。
sed 's/^/.+/' infile | bc | tail -1
ピュアバッシュとワンライナーで:-)
$ 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
Rubyの恋人のために
Ruby -e "puts ARGF.map(&:to_i).inject(&:+)" numbers.txt
代替の純粋なPerl、かなり読みやすい、パッケージやオプションは不要:
Perl -e "map {$x += $_} <> and print $x" < infile.txt
私のバージョン:
seq -5 10 | xargs printf "- - %s" | xargs | bc
あなたが快適に感じるなら、あなたは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()))"
次のようにします(あなたの番号が各行の2番目のフィールドであると仮定します)。
awk 'BEGIN {sum=0} \
{sum=sum + $2} \
END {print "tot:", sum}' Yourinputfile.txt
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)
これを送信することは避けられません。
jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc
それはここで見つけられます:
任意の精度の数のリストを合計するための最も洗練されたunix Shell one-liner?
そしてこれがawk、bc、そして友達よりも優れた特別な利点です。
ラケットのワンライナー:
racket -e '(define (g) (define i (read)) (if (eof-object? i) empty (cons i (g)))) (foldr + 0 (g))' < numlist.txt
C++(簡体字):
echo {1..10} | scc 'WRL n+=$0; n'
SCCプロジェクト - http://volnitsky.com/project/scc/
SCCはシェルプロンプトでのC++スニペット評価者です
バックティックの読みやすさ( "` ")を前もって謝罪しますが、これらは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
}
好みの 'expr'コマンドを使用して、最初に入力を少し細かくするだけで済みます。
seq 10 | tr '[\n]' '+' | sed -e 's/+/ + /g' -e's/ + $/\n/' | xargs expr
プロセスは以下のとおりです。
あなたはいくつかの数クランチタスクの進行状況を監視できるようにするためのリアルタイムの合計。
$ 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にアクセスすることもできません。)
$ 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の答え を参照してください。
完全を期すために、R
ソリューションもあります。
seq 1 10 | R -q -e "f <- file('stdin'); open(f); cat(sum(as.numeric(readLines(f))))"
環境変数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
ありがとう。
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つの簡単な解決策はあなたのためにそれをするためのプログラムを書くことでしょう。これはおそらく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"と入力します。
#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';
}
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'
...とPHPバージョンは、完全を期すためだけのものです。
cat /file/with/numbers | php -r '$s = 0; while (true) { $e = fgets(STDIN); if (false === $e) break; $s += $e; } echo $s;'