Bagaimana saya bisa dengan cepat menjumlahkan semua angka dalam file?


16

Setiap baris berisi teks dan angka dalam satu kolom. Saya perlu menghitung jumlah angka di setiap baris. Bagaimana saya bisa melakukan itu? Terima kasih

example.log berisi:

time=31sec
time=192sec
time=18sec
time=543sec

Jawabannya harus 784


Saya mencoba metode ini awk '{sum + = $ 1}; END {print sum} 'example.log tetapi ini hanya untuk nomor yang sesuai
Jack

2
Ada hampir pertanyaan yang sama di Stack Overflow : Bagaimana saya bisa dengan cepat menjumlahkan semua angka dalam file? . Mungkin saatnya memiliki duplikat lintas-situs?
fedorqui

Jawaban:


18

Jika opsi grepdukungan -oAnda, Anda dapat mencoba:

$ grep -o '[[:digit:]]*' file | paste -sd+ - | bc
784

POSIXly:

$ printf %d\\n "$(( $(tr -cs 0-9 '[\n*]' <file | paste -sd+ -) ))"
784

16

Dengan versi GNU yang lebih baru (4.x) awk:

awk 'BEGIN {FPAT="[0-9]+"}{s+=$1}END{print s}'

Dengan awkpercobaan lain:

awk -F '[a-z=]*' '{s+=$2}END{print s}'

4
Anda perlu s+0dalam kasus di mana skosong, itu akan mencetak 0bukannya kosong.
cuonglm

Biarkan saya jelaskan. - Hanya ada satu case dimana sbisa kosong; jika input data tidak mengandung garis (yaitu jika tidak ada input sama sekali ). Dalam hal ini ada dua perilaku yang mungkin; 1) tidak ada input => tidak ada output, atau 2) selalu mengeluarkan sesuatu, jika hanya 0. Keduanya merupakan opsi yang masuk akal tergantung pada konteks aplikasi. The +0adalah menangani opsi 2). Untuk mengatasi opsi 1) Anda lebih suka menulis END {if(s) print s}. - Oleh karena itu tidak masuk akal untuk mengasumsikan salah satu opsi (untuk kasus sudut ini tidak ada data) sampai ditentukan oleh pertanyaan.
Janis

10
awk -F= '{sum+=$2};END{print sum}'

2
Kami lebih suka jawaban bentuk panjang. Bisakah Anda jelaskan bagaimana ini bekerja?
slm

2
@slm, jawaban itu tidak lebih atau kurang verbose dari jawaban lain di sini dan cukup jelas. Ini juga memiliki keuntungan bekerja dengan input sepertitime=1.4e5sec
Stéphane Chazelas

@ StéphaneChazelas - setuju, tetapi ini adalah pengguna baru dan kami mendorong pengguna untuk memberikan lebih dari satu jawaban baris. Sedikit teks yang menjelaskan cara kerjanya akan membuatnya menjadi jawaban yang jauh lebih kuat dari sekadar kode.
slm

4
@slm, ini adalah pengguna baru dengan salah satu jawaban terbaik (dari sudut pandang teknis) dan dia mendapat dua downvotes dan komentar negatif. Bukan sambutan yang sangat hangat.
Stéphane Chazelas

1
@ TomFenech, sintaks POSIX untuk awk mensyaratkan bahwa item pola / tindakan dipisahkan oleh ";" atau "baris baru", jadi Anda mungkin menemukan implementasi awk ketika gagal tanpa ini ";".
Stéphane Chazelas

7

Satu lagi GNU awk:

awk -v RS='[0-9]+' '{n+=RT};END{print n}'

Satu perl:

perl -lne'$n+=$_ for/\d+/g}{print$n'

Yang POSIX:

tr -cs 0-9 '[\n*]' | grep . | paste -sd + - | bc

6
sed 's/=/ /' file | awk '{ sum+=$2 } END { print sum}'

Jawaban luar biasa, tetapi tidak perlu untuk sed:awk --field-separator = '{ sum+=$2 } END { print sum}' data.dat
user1717828

@ user1717828: Anda sebaiknya menggunakan (lebih pendek, dan lebih kompatibel!) -F'='daripada--field-separator =
Olivier Dulac

@OlivierDulac, aneh, saya man awkhanya memberi -F fsdan--field-separator fs
user1717828

@ user1717828: -F'='atau -F '='ada 2 cara untuk melakukan -F fs(fs adalah "=" dalam kasus Anda). Saya menambahkan singlequotes untuk memastikan fs benar dilihat & ditafsirkan oleh awk, bukan shell (berguna jika fs adalah ';' misalnya)
Olivier Dulac

4

Anda dapat mencoba ini:

awk -F"[^0-9]+" '{ sum += $2 } END { print sum+0; }' file

4

Setiap orang telah memposting awkjawaban yang luar biasa , yang sangat saya sukai.

Variasi untuk @cuonglm diganti grepdengan sed:

sed 's/[^0-9]//g' example.log | paste -sd'+' - | bc
  1. The sedstrip segala sesuatu kecuali untuk nomor.
  2. The paste -sd+ -perintah bergabung semua lini bersama-sama sebagai satu baris
  3. The bcmengevaluasi ekspresi

3

Anda harus menggunakan kalkulator.

{ tr = \ | xargs printf '[%s=]P%d+p' | dc; } <infile 2>/dev/null

Dengan empat baris Anda yang mencetak:

time=31
time=223
time=241
time=784

Dan lebih sederhana:

tr times=c '    + p' <infile |dc

... yang mencetak ...

31
223
241
784

Jika kecepatan adalah apa yang Anda cari maka dcitu yang Anda inginkan. Secara tradisional itu adalah bckompiler - dan masih untuk banyak sistem.


Tidak sesuai dengan pengukuran saya : itu tergantung berapa banyak pekerjaan yang harus Anda lakukan untuk menghasilkan formula
glenn jackman

@glennjackman - pengukuran Anda tidak termasuk sedekat yang dcsaya tahu. Apa yang sedang Anda bicarakan?
mikeserv

Ngomong-ngomong, ketika membandingkan kru lama dengan kru baru - seperti ketika Anda melakukan tolok ukur perlterhadap standar unix toolset - benar-benar tidak masuk akal jika Anda menggunakan alat GNU yang dikompilasi di rantai alat GNU. Semua mengasapi yang secara negatif dapat mempengaruhi kinerja Perl juga di semua util GNU yang dikompilasi. Sedih tapi benar. Anda memerlukan toolset nyata, sederhana dibangun, sederhana untuk menilai perbedaan secara akurat. Sebagai contoh, set alat pusaka yang secara statis terhubung dengan musl libs misalnya - dengan cara itu Anda dapat menempatkan paradigma satu-alat / satu-pekerjaan vs satu-alat-untuk-memerintah-semuanya-semuanya.
mikeserv

3

Melalui python3,

import re
with open(file) as f:
    m = f.read()
    l = re.findall(r'\d+', m)
    print(sum(map(int, l)))

re.findallmengembalikan daftar string, ini tidak akan berfungsi
iruvar

@ 1_CR ya, saya lupa itu. Periksa sekarang.
Avinash Raj

Mungkin sum(int(e) for e in l)lebih pythonic.
cuonglm

3

Solusi bash murni (Bash 3+):

while IFS= read -r line; do                   # While it reads a line:
    if [[ "$line" =~ [0-9]+ ]]; then      # If the line contains numbers:
        ((counter+=BASH_REMATCH[0]))          # Add the current number to counter
    fi                                    # End if.
done                                  # End loop.

echo "Total number: $counter"         # Print the number.
unset counter                         # Reset counter to 0.

Versi pendek:

while IFS= read -r l; do [[ "$l" =~ [0-9]+ ]] && ((c+=BASH_REMATCH)); done; echo $c; c=0

1
Mungkin juga:PS4='$((x+=${time%s*}))' time=0 x=0 sh -x <infile
mikeserv
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.