Xử lý dòng cuối cùng trước bằng cách sử dụng awk


11

Tôi có một tệp dữ liệu mà tôi muốn bình thường hóa bằng cách sử dụng dữ liệu awkcuối cùng. Do đó, tôi muốn truy cập điểm dữ liệu cuối cùng trước, để chuẩn hóa dữ liệu, sau đó xử lý bình thường.

Phương pháp sau đây, sử dụng tachai lần, thực hiện công việc, nhưng, có thể phức tạp hơn mức cần thiết.

$ cat file
0 5
1 2
2 3
3 4
$ tac file | awk 'NR==1{norm=$2} {print $1, $2/norm}' | tac
0 1.25
1 0.5
2 0.75
3 1

Câu hỏi của tôi là như sau: Có thể đạt được kết quả trên chỉ bằng cách sử dụng awk không?

Tôi nghĩ câu trả lời là "Không, awk quét từng dòng tệp", nhưng tôi sẵn sàng cho các đề xuất cho các lựa chọn thay thế.

Câu trả lời:


5

Bạn có thể làm điều đó như một giải pháp hai lượt trong awk:

awk 'FNR == NR { n = $2; next } { print $1, $2/n }' infile infile

Nếu phiên bản awk của bạn hỗ trợ khối ENDFILE (ví dụ: GNU awk 4+), bạn có thể làm như thế này:

awk 'ENDFILE { n = $2 } FNR != NR { print $1, $2/n }' infile infile

Lưu ý rằng seekđến cuối tập tin sẽ hiệu quả hơn trước khi xem câu trả lời của camh .

Giải trình

Ví dụ đầu tiên hoạt động bằng cách ghi nhớ trước đó $2, tức là nó chỉ được đánh giá khi bộ đếm dòng cục bộ ( FNR) bằng với bộ đếm dòng toàn cầu ( NR). Các nextlệnh bỏ qua đến dòng kế tiếp, trong trường hợp này nó đảm bảo rằng khối cuối cùng chỉ được đánh giá khi lập luận thứ hai được phân tách.

Ví dụ thứ hai có logic tương tự, nhưng tận dụng khối ENDFILE được đánh giá khi đạt đến cuối tệp đầu vào.


Ví dụ đầu tiên không hoạt động tốt, thứ hai thì không $ awk --version GNU Awk 3.1.8. Bạn có thể thêm một lời giải thích rất nhỏ về cách xử lý hai tệp đầu vào không và nextlàm gì?
Bernhard

1
@Bernhard: xem chỉnh sửa
Thor

6

Nếu nguồn dữ liệu của bạn là một tệp có thể được đọc nhiều lần (nghĩa là nó không phải là luồng), trước tiên bạn nên sử dụng tail(1)để lấy dữ liệu bạn muốn từ dòng cuối cùng và chuyển nó để xử lý tuần tự tệp. tailsẽ tìm đến cuối tập tin để đọc dòng cuối cùng mà không cần phải đọc tất cả dữ liệu trước nó.

awk -v norm=$(tail -n 1 file | cut -d' ' -f2) '{print $1, $2/norm}' file

Đây sẽ là một chiến thắng lớn trên các tệp lớn trong đó toàn bộ tệp sẽ không vừa trong bộ đệm bộ đệm (có nghĩa là nó sẽ cần được đọc từ đĩa hai lần, một lần cho mỗi lần vượt qua) và sẽ giúp ở mức độ nhỏ hơn bằng cách không cần quét đầu vào để đến dòng cuối cùng. Các tệp nhỏ hơn có thể không hiển thị nhiều khác biệt đối với cách tiếp cận hai lượt.


3

Bạn có thể tải chúng vào một mảng và đọc ngược lại:

awk '{x[i++]=$0} END{for (j=i-1; j>=0;) print x[j--] }'

Bạn có thể làm điều đó hiệu quả hơn, nhưng loại hình này minh họa tại sao awkkhông phải là công cụ phù hợp cho việc này. Tiếp tục sử dụng tackhi có sẵn, GNU tac thường là công cụ nhanh nhất trong số nhiều công cụ cho công việc này.


Tôi đồng ý, sử dụng một for-loops trong awkkhông phải là giải pháp.
Bernhard
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.