shell殼牌 - 高雄shell




Shell命令來整數整數,每行一個? (20)

我正在尋找一個命令,它將接受多行文本作為輸入,每行包含一個整數,並輸出這些整數的總和。

作為一個背景,我有一個日誌文件,其中包括時間測量,所以通過對相關行進行grepping,以及一些sed格式化,我可以列出該文件中的所有時間。 然而,我想要計算出總數,而我的想法卻一片空白,我可以用這個中間輸出來執行最後的總和。 過去我一直使用expr ,但除非它運行在RPN mode ,否則我不認為它會應付這種情況(即使這樣也會很棘手)。

我錯過了什麼? 考慮到可能有幾種方法可以實現這一目標,即使其他人已經發布了可以完成這項工作的不同解決方案,我也會很樂意閱讀(並upvote )任何可行的方法。

相關問題: 最短命令來計算在Unix上的一列輸出的總和? (學分@Andrew

更新 :哇,正如所料,這裡有一些很好的答案。 看起來,我一定會作為一般的command-line tool來深入檢查awk


AWK已經被提及,所以另外我想建議你使用這種語言代替 GREP和SED來掃描原始日誌文件。 一個合適的AWK腳本可以很容易地完成兩者的工作,並且可以像Paul和Alf所指出的那樣計算出有趣的值。


BASH解決方案,如果你想讓這個命令(例如,如果你需要經常這樣做):

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

然後用法:

addnums < /tmp/nums

Python中的單行版本:

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

awk位應該這樣做?

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

注意:如果您要添加超過2 ^ 31(2147483647)的任何內容,awk的某些版本會有一些奇怪的行為。 查看更多背景的評論。 一個建議是使用printf而不是print

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

以下應該工作(假設你的號碼是每行的第二個字段)。

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

你可以使用num-utils,雖然它可能是你需要的矯枉過正。 這是一套用於在shell中操縱數字的程序,可以做幾件漂亮的事情,當然包括添加它們。 這有點過時了,但是如果你需要做更多的事情,它們仍然可以工作,並且可以是有用的。

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


實時總結,讓您監控某些數字處理任務的進度。

$ 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。)


對於Ruby戀人

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

我已經對現有的答案做了一個快速的基準

  • 只使用標準工具(抱歉,像luarocket類的東西),
  • 是真正的單線,
  • 能夠添加大量的數字(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

Paste&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當然是8s中最快的,但Pypy解決方案僅增加了30%到11%的很小開銷 。 但是,公平地說,Pypy並不完全是標準的。 大多數人只安裝了CPython,其速度明顯較慢(22s),與流行的Awk解決方案一樣快。

基於標準工具的最快解決方案是Perl(15s)。


我意識到這是一個古老的問題,但我喜歡這個解決方案足以分享它。

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

我的版本:

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

替代純Perl,相當可讀,不需要包或選項:

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

球拍中的單線:

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

簡單的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

簡單的打擊一個班輪

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

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

純粹的打擊和單線:-)

$ 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

純粹的短打擊。

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

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

請注意,以負號為前綴的負數應翻譯為dc ,因為它使用_前綴而不是-前綴。 例如,通過tr '-' '_' | dc -f- -e '...' 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. 如果堆棧有兩個或更多值,請遞歸運行“add_top_of_stack”
  2. 如果堆棧有兩個或更多值,請運行“add_top_of_stack”
  3. 打印結果,現在是堆棧中剩下的唯一項目

要真正理解dc的簡單性和強大功能,下面是一個可用的Python腳本,它實現了來自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()

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




shell