คำสั่ง Shell เพื่อรวมจำนวนเต็มหนึ่งรายการต่อบรรทัดหรือไม่


867

ฉันกำลังมองหาคำสั่งที่จะยอมรับ (เป็นอินพุต) ข้อความหลายบรรทัดแต่ละบรรทัดมีจำนวนเต็มเดียวและส่งออกผลรวมของจำนวนเต็มเหล่านี้

เป็นพื้นหลังเล็กน้อยฉันมีไฟล์บันทึกซึ่งรวมถึงการวัดเวลา ผ่านการ grepping สำหรับบรรทัดที่เกี่ยวข้องและการsedฟอร์แมตเล็กน้อยฉันสามารถแสดงรายการการกำหนดเวลาทั้งหมดในไฟล์นั้น ฉันต้องการผลรวมทั้งหมด ฉันสามารถไพพ์เอาต์พุตกลางนี้ไปยังคำสั่งใดก็ได้เพื่อทำผลรวมสุดท้าย ฉันเคยใช้มาก่อนexprในอดีต แต่ถ้ามันไม่ทำงานในโหมด RPN ฉันไม่คิดว่ามันจะรับมือกับสิ่งนี้ (และถึงแม้มันจะยุ่งยากก็ตาม)

ฉันจะรับผลรวมของจำนวนเต็มได้อย่างไร


2
นี่คล้ายกับคำถามที่ฉันถามเมื่อนานมาแล้ว: stackoverflow.com/questions/295781/…
An̲̳̳drew

5
ฉันชอบคำถามนี้เพราะมีคำตอบที่ถูกต้อง (หรืออย่างน้อยก็ใช้ได้) ที่เป็นไปได้
Francisco Canedo

คำถามนี้รู้สึกเหมือนเป็นปัญหาสำหรับรหัสกอล์ฟ codegolf.stackexchange.com :)
Gordon Bean

คำตอบ:


1322

บิตของ awk ควรทำหรือไม่

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

หมายเหตุ: awk บางรุ่นมีพฤติกรรมแปลก ๆ บางอย่างถ้าคุณจะเพิ่มอะไรเกิน 2 ^ 31 (2147483647) ดูความคิดเห็นสำหรับพื้นหลังเพิ่มเติม หนึ่งข้อเสนอแนะคือการใช้printfมากกว่าprint:

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

7
มีความรักมากมายในห้องนี้! ผมชอบวิธีการสคริปต์ง่ายๆเช่นนี้อาจมีการปรับเปลี่ยนเพื่อเพิ่มขึ้นคอลัมน์ที่สองของข้อมูลเพียงโดยการเปลี่ยน $ 1 $ 2
พอลดิกสัน

2
ไม่มีข้อ จำกัด ในทางปฏิบัติเนื่องจากมันจะประมวลผลอินพุตเป็นสตรีม ดังนั้นถ้ามันสามารถจัดการไฟล์ของเส้น X ได้คุณค่อนข้างมั่นใจว่ามันสามารถจัดการ X + 1 ได้
Paul Dixon

4
ฉันเคยเขียนโพรเซสซิงรายชื่อผู้รับจดหมายขั้นพื้นฐานที่มีสคริปต์ awk รันผ่านยูทิลิตี้วันหยุด ช่วงเวลาที่ดี. :)
LS

2
เพิ่งใช้สิ่งนี้เพื่อ: นับสคริปต์หน้าเอกสารทั้งหมด:ls $@ | xargs -i pdftk {} dump_data | grep NumberOfPages | awk '{s+=$2} END {print s}'
flying flying

8
ระวังมันจะไม่ทำงานกับตัวเลขที่มากกว่า 2147483647 (เช่น 2 ^ 31) นั่นเป็นเพราะ awk ใช้การแทนจำนวนเต็ม 32 บิต ใช้awk '{s+=$1} END {printf "%.0f", s}' mydatafileแทน
Giancarlo Sportelli

665

วางโดยทั่วไปแล้วผสานบรรทัดของไฟล์หลาย ๆ ไฟล์ แต่ก็สามารถใช้ในการแปลงแต่ละบรรทัดของไฟล์เป็นบรรทัดเดียว ตัวคั่นธงอนุญาตให้คุณผ่านสมการชนิด x + x ไปยัง bc

paste -s -d+ infile | bc

อีกทางเลือกหนึ่งเมื่อท่อจาก stdin

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

1
ดีมาก! ฉันจะใส่ช่องว่างหน้าเครื่องหมาย "+" เพียงเพื่อช่วยฉันแยกวิเคราะห์ได้ดีกว่า
Michael H.

73
จดจำและพิมพ์ได้ง่ายกว่าโซลูชัน awk มาก นอกจากนี้โปรดทราบว่าpasteสามารถใช้เส้นประ-เป็นชื่อไฟล์ - ซึ่งจะช่วยให้คุณไพพ์ตัวเลขจากเอาต์พุตของคำสั่งไปยังเอาต์พุตมาตรฐานของ paste โดยไม่จำเป็นต้องสร้างไฟล์ก่อน:<commands> | paste -sd+ - | bc
George

19
ฉันมีไฟล์ที่มีตัวเลข 100 ล้าน คำสั่ง awk ใช้เวลา 21 วินาที คำสั่ง paste ใช้เวลา 41 วินาที แต่ก็ยังดีที่เจอ 'แปะ'!
Abhi

4
@Abhi: ที่น่าสนใจ: เดาว่ามันจะพาฉันไปยุค 20 คิดหาคำสั่ง awk แม้ว่ามันจะออกมาจนกว่าฉันจะลอง 100 ล้านและตัวเลขหนึ่ง: D
Mark K Cowan

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

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))"
Paul Paul

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

9
เพราะปัญหานี้เป็นจริงในฟังก์ชั่น "พิมพ์" Awk ใช้จำนวนเต็ม 64 บิต แต่ด้วยเหตุผลบางอย่างที่พิมพ์ไม่ได้ให้เป็น 32 บิต
Giancarlo Sportelli

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
แพทริค

1
ไม่ควรใช้แค่ "% ld" บนระบบ 64 บิตเพื่อให้ printf truncate เป็น 32 บิตไม่ได้ใช่ไหม เมื่อ @Patrick ชี้ให้เห็นลอยไม่ใช่ความคิดที่ดีที่นี่
yerforkferchips

78

ทุบตีธรรมดา:

$ 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

2
ซับขนาดเล็กกว่าหนึ่งตัว: stackoverflow.com/questions/450799/…
Khaja Minhajuddin

@rjack มีการnumกำหนดไว้ที่ไหน? ฉันเชื่อว่าอย่างใดมันเชื่อมต่อกับการ< numbers.txtแสดงออก แต่ไม่ชัดเจนว่า
Atcold

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. หากสแต็กมีสองค่าขึ้นไปให้รัน "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()

2
dc เป็นเพียงเครื่องมือในการเลือกใช้ แต่ฉันจะทำมันด้วย ops สแต็กน้อยกว่าเล็กน้อย (echo "0"; sed 's/$/ +/' inp; echo 'pq')|dcสันนิษฐานว่าทุกบรรทัดจริงๆมีจำนวน:
ikrabbe

5
dc -e '0 0 [+?z1<m]dsmxp'อัลกอริทึมที่ออนไลน์: ดังนั้นเราจึงไม่บันทึกหมายเลขทั้งหมดในสแต็กก่อนการประมวลผล แต่อ่านและประมวลผลทีละรายการ (เพื่อความแม่นยำมากขึ้นทีละบรรทัดเนื่องจากหนึ่งบรรทัดสามารถมีตัวเลขได้หลายหมายเลข) โปรดทราบว่าบรรทัดว่างสามารถยุติลำดับอินพุต
ruvim

@ikrabbe เยี่ยมมาก จริง ๆ แล้วสามารถย่อให้สั้นได้อีกหนึ่งอักขระ: sedสามารถลบช่องว่างในการแทนที่ได้เนื่องจากdc ไม่สนใจช่องว่างระหว่างอาร์กิวเมนต์และตัวดำเนินการ (echo "0"; sed 's/$/+/' inputFile; echo 'pq')|dc
WhiteHotLoveTiger

58

ด้วยjq :

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

7
ฉันชอบสิ่งนี้เพราะฉันคิดว่ามันชัดเจนและสั้นมากจนฉันอาจจำได้
Alfe

46

ทุบตีบริสุทธิ์และสั้น

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

9
f=$(<numbers.txt)นี่คือทางออกที่ดีที่สุดเพราะมันไม่ได้สร้างกระบวนการย่อยประสงค์หากแทนที่บรรทัดแรกที่มี
loentar

1
มีวิธีป้อนข้อมูลจาก stdin หรือไม่? ชอบจากท่อหรือไม่?
njzk2

@ njzk2 หากคุณใส่f=$(cat); echo $(( ${f//$'\n'/+} ))สคริปต์คุณสามารถไพพ์อะไรก็ได้ในสคริปต์นั้นหรือเรียกใช้โดยไม่มีข้อโต้แย้งสำหรับอินพุต stdin แบบโต้ตอบ (ยุติด้วย Control-D)
mklement0

5
@loentar <numbers.txtเป็นการปรับปรุง แต่โดยรวมแล้วโซลูชันนี้มีประสิทธิภาพสำหรับไฟล์อินพุตขนาดเล็กเท่านั้น ตัวอย่างเช่นด้วยไฟล์ 1,000 บรรทัดอินพุตawkโซลูชันที่ยอมรับนั้นเร็วกว่าประมาณ 20 เท่าบนเครื่องของฉันและยังใช้หน่วยความจำน้อยลงเพราะไฟล์ไม่อ่านทั้งหมดในครั้งเดียว
mklement0

2
ฉันเกือบหมดหวังเมื่อฉันมาถึงที่นี่ ทุบตีบริสุทธิ์!
Omer Akhter

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

4
และฉันได้เพิ่มพวกเขากลับ: "-l" ทำให้มั่นใจได้ว่าเอาต์พุตจะถูกยกเลิก LF เป็นเชลล์ `` backticks และโปรแกรมส่วนใหญ่คาดหวังและ "<" หมายถึงคำสั่งนี้สามารถใช้ในไพพ์ไลน์
j_random_hacker

คุณพูดถูก ในฐานะที่เป็นข้อแก้ตัว: ตัวละครใน Perl one-liners แต่ละคนต้องการงานจิตสำหรับฉันดังนั้นฉันชอบที่จะดึงตัวละครให้ได้มากที่สุด นิสัยเป็นอันตรายในกรณีนี้
jfs

2
หนึ่งในไม่กี่โซลูชั่นที่ไม่โหลดทุกสิ่งลงใน RAM
Erik Aronesty

28

สิบห้าเซ็นต์ของฉัน:

$ 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)
  • เป็นหนึ่งในตอร์ปิโดจริง
  • มีความสามารถในการเพิ่มจำนวนมาก (100 ล้าน) และ
  • รวดเร็ว (ฉันไม่สนใจสิ่งที่ใช้เวลานานกว่าหนึ่งนาที)

ฉันมักจะเพิ่มจำนวน 1 ถึง 100 ล้านซึ่งสามารถทำได้บนเครื่องของฉันในเวลาน้อยกว่าหนึ่งนาทีสำหรับการแก้ปัญหาหลายอย่าง

นี่คือผลลัพธ์:

หลาม

:; 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

ดังนั้นฉันเดาว่าน่าจะใช้เวลาประมาณ 35 ถึง 100 ล้านตัว

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 นั้นเร็วที่สุดใน 8 วินาที แต่โซลูชัน Pypy เพิ่มค่าใช้จ่ายเพียงเล็กน้อยประมาณ 30% ถึง 11วินาที แต่เพื่อความยุติธรรม Pypy นั้นไม่ได้มาตรฐานอย่างแน่นอน คนส่วนใหญ่มีการติดตั้ง CPython ซึ่งช้ากว่าอย่างมาก (22 ปี) ซึ่งเร็วเท่ากับโซลูชัน Awk ที่ได้รับความนิยม

วิธีที่เร็วที่สุดที่ใช้เครื่องมือมาตรฐานคือ Perl (15s)


2
paste+ bcวิธีการก็แค่สิ่งที่ผมกำลังมองหาผลรวมค่า hex ขอบคุณ!
Tomislav Nakic-Alfirevic

1
เพียงเพื่อความสนุกสนานใน Rust: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); }
โจเซลีน

คำตอบที่น่ากลัว ไม่ใช่เพื่อ nitpick แต่เป็นกรณีที่ถ้าคุณตัดสินใจที่จะรวมผลลัพธ์ที่ยาวกว่านั้นคำตอบจะยอดเยี่ยมยิ่งขึ้น!
Steven Lu

@StevenLu ฉันรู้สึกว่าคำตอบจะนานและน่ากลัวน้อยกว่า (ใช้คำของคุณ) แต่ฉันสามารถเข้าใจได้ว่าความรู้สึกนี้ไม่จำเป็นต้องแชร์กับทุกคน :)
Alfe

ถัดไป: numba + parallelisation
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ไม่ได้เป็น "Bash ธรรมดา" / nitpick
Benjamin W.

17

วิธีการแก้ปัญหาทุบตีถ้าคุณต้องการที่จะทำให้คำสั่งนี้ (เช่นถ้าคุณจำเป็นต้องทำบ่อยครั้ง):

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
มันเป็นเรื่องที่ซ้ำซ้อน: stackoverflow.com/questions/450799/…
jfs

11

งานต่อไปนี้ในทุบตี:

I=0

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

echo $I

1
การขยายคำสั่งควรใช้ด้วยความระมัดระวังเมื่อไฟล์มีขนาดใหญ่โดยพลการ ด้วย numbers.txt 10MB cat numbers.txtขั้นตอนจะเป็นปัญหา
Giacomo

1
อย่างไรก็ตามแน่นอน (หากไม่ใช่เพื่อการแก้ปัญหาที่ดีกว่าพบที่นี่) ฉันจะใช้อันนี้จนกว่าฉันจะพบปัญหาจริง
Francisco Canedo

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 และความหมายเป็นสิ่งที่ดีไม่ได้เป็นเพียงบางส่วนวางสตริงฉลาด;)
ฮอบส์

2
ใช่ได้โปรดอธิบาย :) (ฉันไม่ใช่คนประเภท Perl)
Jens

1
ลองเรียกใช้ "perl -MO = Deparse -lpe '$ c + = $ _} {$ _ = $ c'" และดูผลลัพธ์โดยทั่วไป -l ใช้บรรทัดใหม่และตัวคั่นอินพุตและเอาต์พุตและ -p พิมพ์แต่ละบรรทัด แต่เพื่อที่จะทำ '-p', Perl แรกเพิ่มจานหม้อไอน้ำ (ซึ่ง -MO = Deparse) จะแสดงให้คุณเห็น แต่แล้วมันก็เป็นเพียงทดแทนและรวบรวม คุณสามารถทำให้ส่วนเพิ่มเติมของบล็อกถูกแทรกด้วยส่วน '} {' และหลอกให้บล็อกไม่ใช่การพิมพ์ในแต่ละบรรทัด แต่พิมพ์ที่ส่วนท้ายสุด
Nym

9

ทุบตีบริสุทธิ์และในหนึ่งซับ :-)

$ 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

ทำไมจึงมีสอง((วงเล็บ))?
Atcold

ไม่ใช่ทุบตีอย่างแท้จริงเนื่องจากแมว ทำให้ทุบตีบริสุทธิ์โดยการเปลี่ยนแมวด้วย$(< numbers.txt)
Dani_l


6

ทางเลือกเพียวเพอร์เพิลสามารถอ่านได้อย่างเป็นธรรมไม่มีแพ็คเกจหรือตัวเลือกที่ต้องการ:

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

หรือสั้นกว่านิดหน่อย: แผนที่ perl -e '{$ x + = $ _} <>; พิมพ์ $ x 'infile.txt
Avi Tevet

หน่วยความจำที่ต้องการคือเกือบ 2GB สำหรับการป้อนข้อมูลขนาดใหญ่จำนวน 10 ล้านหมายเลข
Amit Naidu


5

ไม่สามารถหลีกเลี่ยงการส่งสิ่งนี้:

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

พบได้ที่นี่:
เปลือกยูนิกซ์ที่หรูหราที่สุดหนึ่งซับในรายการผลรวมของจำนวนความแม่นยำโดยพลการ?

และนี่คือข้อดีพิเศษของ 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

คุณสามารถทำได้ในไพ ธ อนถ้าคุณรู้สึกสะดวกสบาย:

ไม่ได้ทดสอบพิมพ์เพียง:

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

หลาม -c "จากอินพุตอินพุตไฟล์อินพุต, ผลรวมการพิมพ์ (แผนที่ (int, อินพุต ()))" numbers.txt
jfs

2
cat ใช้งานมากเกินไปเปลี่ยนเส้นทาง stdin จากไฟล์: python -c "... " <numbers.txt
Giacomo

2
@rjack: catใช้เพื่อแสดงให้เห็นว่าสคริปต์ทำงานได้ทั้ง stdin และสำหรับไฟล์ใน argv [] (เหมือนwhile(<>)ใน Perl) หากอินพุตของคุณอยู่ในไฟล์แสดงว่า '<' ไม่จำเป็น
jfs

2
แต่< numbers.txtแสดงให้เห็นว่าการทำงานบน stdin เพียงเป็นcat numbers.txt |ไม่ และมันก็ไม่ได้สอนนิสัยที่ไม่ดี
Xiong Chiamiov

3
$ 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ที่หลีกเลี่ยงการ slurping


3

ข้อมูลต่อไปนี้ควรใช้งานได้ (สมมติว่าหมายเลขของคุณเป็นฟิลด์ที่สองในแต่ละบรรทัด)

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

2
คุณไม่ต้องการส่วน {sum = 0}
จริงๆ

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

ขออภัยล่วงหน้าสำหรับการอ่าน backticks ("` ") แต่การทำงานเหล่านี้ในเชลล์นอกเหนือจาก bash และทำให้สามารถวางได้มากกว่า หากคุณใช้เชลล์ที่ยอมรับรูปแบบ $ (คำสั่ง ... ) สามารถอ่านได้มากขึ้น (และสามารถ debuggable) ได้มากกว่าคำสั่ง `` ... 'ดังนั้นอย่าลังเลที่จะแก้ไขสำหรับสติของคุณ

ฉันมีฟังก์ชั่นที่เรียบง่ายใน 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

นั่นคือถ้าคุณต้องการที่จะรวมพวกเขาทีละบรรทัด อย่างไรก็ตามสำหรับจำนวนทุกจำนวนใน datafile

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

btw ถ้าฉันต้องการทำบางสิ่งบางอย่างอย่างรวดเร็วบนเดสก์ท็อปฉันใช้สิ่งนี้:

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

2
คุณมีอะไรชนิดของเปลือกหอยโบราณใช้ที่ไม่สนับสนุน$()?
nyuszika7h
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.