Làm thế nào để lấy giá trị tuyệt đối bằng awk?


14

Nếu tôi có dưới hai ngày:

2015-09-12,2015-08-13

Và tôi cần lấy số ngày giữa chúng, tôi sẽ sử dụng đoạn mã dưới đây:

awk -F'[-,]' '{print 360*($4-$1)+30*($5-$2)+($6-$3)}'

Đầu ra cho mã này sẽ là -29trong khi thực tế sự khác biệt là29

Câu trả lời:


23

Bạn có thể định nghĩa các hàm awknhư:

awk -F'[-,]' '
  function abs(v) {return v < 0 ? -v : v}
  {print abs(360*($4-$1)+30*($5-$2)+($6-$3))}'

11

Thủ thuật phổ biến cho loại tình huống này là sử dụng căn bậc hai của hình vuông:

awk -F'[-,]' '{print sqrt((360*($4-$1)+30*($5-$2)+($6-$3))^2)}'

3
Một chút quá mức mặc dù. Lưu ý sqrt(x^2)là tốt, nhưng sqrt(x)^2có thể giới thiệu các lỗi nhỏ có thể gây ra sự ngạc nhiên. Đối với busybox awk, nó cần được xây dựng với tính năng hỗ trợ toán học được bật (không phải mặc định trong các gói Debian).
Stéphane Chazelas

3
Sẽ không sqrt (x) ^ 2 đơn giản là thất bại cho các số âm?
Daniel McLaury

1
@DanielMcLaury Đó là lý do tại sao sqrt(x^2).
jimmij

@jimmij: Tôi đang trả lời nhận xét về câu trả lời của bạn, không phải cho câu trả lời.
Daniel McLaury

3

Cách khác:

awk -F'[-,]' '{d=360*($4-$1)+30*($5-$2)+($6-$3);print (d>0)?d:-d}'

Đây có lẽ là câu trả lời (hiệu suất) hiệu quả nhất.
Hastur

2

Giả sử bạn đang sử dụng GNU awk, mktimechức năng thú vị có ích ở đây.

awk -F, '{ gsub(/-/," ",$0);a=(mktime($2 " 23 59 59")-mktime($1 " 00 00 00"))/86400;print a*(a<0?-1:1)}' file.txt
29

1

Đến muộn, nhưng đây là một giải pháp sử dụng datelệnh GNU không dựa trên 30 ngày cố định mỗi tháng mà tất cả các câu trả lời được đăng ở trên đều coi đó là câu trả lời của steve .

awk -F, '{cmd="printf \"%d\n\" $((($(date -d"$1" +%s)-$(date -d"$2" +%s))/86400))"; 
    cmd|getline $0; $0*=($0<0?-1:1); close(cmd)}1' infile

Đối với đầu vào dưới đây:

2015-09-12,2015-08-13
2017-02-12,2017-03-12

Đầu ra là:

30
28
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.