Sử dụng awk để tính tổng các giá trị của một cột, dựa trên các giá trị của cột khác


63

Tôi đang cố gắng tính tổng số nhất định trong một cột bằng cách sử dụng awk. Tôi muốn tổng hợp chỉ cột 3 của "smiths" để có tổng cộng là 212. Tôi có thể tổng hợp toàn bộ cột bằng cách sử dụng awkchứ không chỉ là "smiths". Tôi có:

awk 'BEGIN {FS = "|"} ; {sum+=$3} END {print sum}' filename.txt

Ngoài ra tôi đang sử dụng putty. Cảm ơn bạn đã giúp đỡ.

smiths|Login|2
olivert|Login|10
denniss|Payroll|100
smiths|Time|200
smiths|Logout|10

Câu trả lời:


81
awk -F '|' '$1 ~ /smiths/ {sum += $3} END {print sum}' inputfilename
  • Các -Flá cờ đặt tách lĩnh vực; Tôi đặt nó trong dấu ngoặc đơn vì nó là một ký tự vỏ đặc biệt.
  • Sau đó, chỉ $1 ~ /smiths/áp dụng {mã khối} sau cho các dòng có trường đầu tiên khớp với biểu thức chính quy /smiths/.
  • Phần còn lại giống như mã của bạn.

Lưu ý rằng vì bạn không thực sự sử dụng regex ở đây, chỉ là một giá trị cụ thể, bạn có thể dễ dàng sử dụng:

awk -F '|' '$1 == "smiths" {sum += $3} END {print sum}' inputfilename

Mà kiểm tra bình đẳng chuỗi. Điều này tương đương với việc sử dụng regex /^smiths$/, như đã đề cập trong một câu trả lời khác, bao gồm ^neo để chỉ khớp với phần bắt đầu của chuỗi (bắt đầu của trường 1) và phần $neo để chỉ khớp với phần cuối của chuỗi. Không chắc bạn quen thuộc với regexes như thế nào. Chúng rất mạnh, nhưng trong trường hợp này, bạn có thể sử dụng kiểm tra tính bằng chuỗi một cách dễ dàng.


3
Nhân tiện, tài liệu tham khảo awk yêu thích của tôi là grymoire.com/Unix/Awk.html . Trang rất hữu ích.
tự đại diện

1
Cảm ơn bạn @Wildcard! Tôi đã có thể tổng hợp gọn gàng kích thước không nén của các tệp cụ thể trong kho lưu trữ zip lớn dựa trên lời khuyên của bạn :) unzip -lv /appl/tmp/data.lar | grep documentlibrary | awk '{sum += $1} END {print sum/1024/1024}'
Pawel

15

Một cách tiếp cận khác là sử dụng mảng kết hợp awk, thông tin thêm ở đây . Dòng này tạo ra đầu ra mong muốn:

awk -F '|' '{a[$1] += $3} END{print a["smiths"]}' filename.txt

Là một tác dụng phụ, mảng lưu trữ tất cả các giá trị khác:

awk -F '|' '{a[$1] += $3} END{for (i in a) print i, a[i]}' filename.txt

Đầu ra:

smiths 212
denniss 100
olivert 10

Đây là câu trả lời đúng
PoVa 24/03/18

5

Rất tốt cho đến nay. Tất cả bạn cần làm là thêm một bộ chọn trước khối để thêm tổng. Ở đây chúng tôi kiểm tra rằng đối số đầu tiên chỉ chứa "smiths":

awk 'BEGIN {FS = "|"} ; $1 ~ /^smiths$/ {sum+=$3} END {print sum}'

Bạn có thể rút ngắn điều này một chút bằng cách chỉ định dấu tách trường làm tùy chọn. Nói awkchung, đó là một ý tưởng tốt để khởi tạo các biến trên dòng lệnh:

awk -F'|' '$1 ~ /^smiths$/ {sum+=$3} END {print sum}'

0
cat filename.txt | grep smiths | awk -F '|' '{sum+=$NF} END {print sum}'
  • -F tùy chọn để chỉ định dấu phân cách.
  • $NF là cho "cột cuối cùng".

1
catgrepkhông cần thiết ở đây.
Andrey

Tại sao grep không cần thiết @Andrey? OP chỉ muốn thêm các hàng "smiths". Bạn sẽ cần phải sửa đổi tuyên bố awk, phải không?
EL

1
@EL có, câu lệnh awk nên được sửa đổi /smiths/{...}nếu cuộc gọi grep không có ở đó. Đây là một sửa đổi nhỏ, nhưng nó mang lại lợi ích đáng kể: giảm số lượng quy trình đang chạy, đơn giản hóa việc kiểm soát lỗi và làm cho mã rõ ràng hơn.
Andrey

0

Cá nhân tôi muốn giữ awkphần đơn giản nhất có thể và làm nhiều nhất có thể mà không cần nó. Logic đi kèm không tận dụng được sức mạnh của các đường ống Unix và do đó khó hiểu hơn, gỡ lỗi hoặc sửa đổi cho các trường hợp sử dụng liên quan chặt chẽ.

cat filename.txt | perl -pe 's{.*|}{}g' | awk '{sum+=$1} END {print sum}'
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.