Làm thế nào tôi có thể nhanh chóng tổng hợp tất cả các số trong một tập tin?


16

Mỗi dòng chứa văn bản và số trong một cột. Tôi cần tính tổng của các số trong mỗi hàng. Làm thế nào tôi có thể làm điều đó? Cám ơn

example.log chứa:

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

Câu trả lời phải là 784


Tôi đã thử phương pháp này awk '{sum + = $ 1}; END {print sum} 'example.log nhưng nó chỉ dành cho các số trong dòng
Jack

2
Gần như có một câu hỏi tương tự trong Stack Overflow : Làm thế nào tôi có thể tổng hợp nhanh chóng tất cả các số trong một tệp? . Có lẽ thời gian để có bản sao chéo trang web?
fedorqui

Câu trả lời:


18

Nếu tùy chọn grephỗ trợ của bạn -o, bạn có thể thử:

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

POSIXly:

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

16

Với phiên bản mới hơn (4.x) của GNU awk:

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

Với những awkthử khác:

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

4
Bạn cần s+0trong trường hợp strống, nó sẽ in 0thay vì trống.
cuonglm

Hãy để tôi giải thích điều đó. - Chỉ có một trường hợp scó thể để trống; nếu dữ liệu đầu vào không chứa dòng nào (tức là nếu không có đầu vào nào cả ). Trong trường hợp đó có hai hành vi có thể; 1) không có đầu vào => không có đầu ra hoặc 2) luôn xuất ra thứ gì đó, nếu chỉ 0. Cả hai đều là các tùy chọn hợp lý tùy thuộc vào ngữ cảnh ứng dụng. Đây +0là địa chỉ tùy chọn 2). Để giải quyết tùy chọn 1) bạn muốn viết END {if(s) print s}. - Do đó, sẽ không có ý nghĩa gì khi giả sử một trong hai tùy chọn (đối với trường hợp góc không có dữ liệu này) cho đến khi được chỉ định bởi câu hỏi.
Janis

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

2
Chúng tôi thích câu trả lời dài. Bạn có thể vui lòng giải thích cách làm việc này?
slm

2
@slm, câu trả lời đó không nhiều hoặc ít dài dòng hơn các câu trả lời khác ở đây và là tự giải thích. Nó cũng có lợi thế khi làm việc với đầu vào nhưtime=1.4e5sec
Stéphane Chazelas

@ StéphaneChazelas - đã đồng ý, nhưng đây là một người dùng mới và chúng tôi khuyến khích người dùng cung cấp nhiều hơn các câu trả lời đơn. Một chút văn bản giải thích cách thức hoạt động của nó sẽ làm cho nó trở thành một câu trả lời mạnh mẽ hơn nhiều so với chỉ mã.
slm

4
@slm, đây là một người dùng mới với một trong những câu trả lời hay nhất (từ quan điểm kỹ thuật) và anh ta nhận được hai lượt tải xuống và nhận xét tiêu cực. Không được chào đón nồng nhiệt.
Stéphane Chazelas

1
@TomFenech, cú pháp POSIX cho awk yêu cầu các mục hành động / mẫu đó phải được phân tách bằng dấu ";" hoặc "dòng mới", vì vậy bạn có thể tìm thấy các triển khai awk khi nó không thành công nếu không có ";".
Stéphane Chazelas

7

Một GNU khác awk:

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

Một perlmột:

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

Một POSIX:

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

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

Câu trả lời tuyệt vời, nhưng không cần sed:awk --field-separator = '{ sum+=$2 } END { print sum}' data.dat
user1717828 27/05/2015

@ user1717828: bạn nên sử dụng (ngắn hơn và tương thích hơn!) -F'='thay vì--field-separator =
Olivier Dulac

@OlivierDulac, thật lạ, tôi man awkchỉ cho -F fs--field-separator fs
dùng1717828

@ user1717828: -F'='hoặc -F '='là 2 cách thực hiện -F fs(fs là "=" trong trường hợp của bạn). Tôi đã thêm các câu đơn để đảm bảo fs được nhìn thấy và giải thích chính xác bởi awk, chứ không phải shell (ví dụ nếu fs là ';' chẳng hạn)
Olivier Dulac

4

Bạn có thể thử điều này:

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

4

Mọi người đã đăng awkcâu trả lời tuyệt vời , mà tôi rất thích.

Một biến thể để @cuonglm thay thế grepbằng sed:

sed 's/[^0-9]//g' example.log | paste -sd'+' - | bc
  1. Các seddải tất cả mọi thứ trừ các con số.
  2. Các paste -sd+ -lệnh tham gia tất cả các dòng với nhau như một dòng duy nhất
  3. Các bcđánh giá biểu thức

3

Bạn nên sử dụng một máy tính.

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

Với bốn dòng của bạn in:

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

Và đơn giản hơn:

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

... mà in ...

31
223
241
784

Nếu tốc độ là thứ bạn đang theo đuổi thì đó dclà thứ bạn muốn. Theo truyền thống, nó là bctrình biên dịch - và vẫn dành cho nhiều hệ thống.


Không theo số đo của tôi : nó phụ thuộc vào số lượng công việc bạn phải làm để tạo công thức
glenn jackman

@glennjackman - số đo của bạn không bao gồm dcgần như tôi có thể nói. Bạn đang nói về cái gì vậy?
mikeerv

Nhân tiện, khi so sánh phi hành đoàn cũ với phi hành đoàn mới - chẳng hạn như khi bạn điểm chuẩn perlv bộ công cụ unix tiêu chuẩn - thực sự không có ý nghĩa gì nếu bạn sử dụng các công cụ GNU được biên dịch trên chuỗi công cụ GNU. Tất cả sự phình to có thể ảnh hưởng tiêu cực đến hiệu suất của Perl cũng nằm trong tất cả các tiện ích GNU được biên dịch bởi GNU. Đáng buồn nhưng là sự thật. Bạn cần một bộ công cụ thực sự, được xây dựng đơn giản, đơn giản để đánh giá chính xác sự khác biệt. Ví dụ như một bộ công cụ gia truyền được liên kết tĩnh với các lib musl - theo cách đó bạn có thể điều chỉnh mô hình một công cụ / một công việc so với mô hình một công cụ để thống trị tất cả một.
mikeerv

3

Qua python3,

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

re.findalltrả về một danh sách các chuỗi, điều này sẽ không hoạt động
iruvar

@ 1_CR ya, tôi quên điều đó. Kiểm tra nó ngay bây giờ.
Avinash Raj

Có lẽ sum(int(e) for e in l)là nhiều pythonic.
cuonglm

3

Dung dịch bash tinh khiết (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.

Phiên bản ngắn:

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

1
Cũng có thể:PS4='$((x+=${time%s*}))' time=0 x=0 sh -x <infile
mikeerv
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.