Khi sử dụng awk / mẫu / {print văn bản Khăn}


22

Giả sử tôi có tệp văn bản như:

R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

Tôi muốn sử dụng awkđể xử lý các dòng này khác nhau, như

awk '/R1/ { print "=>" $0} /R2/ { print "*" $0} '

và tôi cũng muốn in tất cả các dòng còn lại như hiện tại (không tạo các bản sao của các dòng tôi đã xử lý), về cơ bản tôi cần một dòng /ELSE/ { print $0}ở cuối awkdòng.

Có một điều như vậy?

Câu trả lời:


27

Phương pháp đơn giản hóa với awk

awk '/R1/ {print "=>" $0;next} /R2/{print "*" $0;next} 1' text.file

[jaypal:~/Temp] cat text.file 
R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

[jaypal:~/Temp] awk '/R1/ { print "=>" $0;next} /R2/{print "*" $0;next}1' text.file
=>R1 12 324 3453 36 457 4 7 8
*R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242
[jaypal:~/Temp] 

Báo cáo đột phá của mẫu {Action}:

  • /R1/ { print "=>" $0;next}: Điều này có nghĩa là các dòng có /R1/hành động in =>sẽ được thực hiện. nextcó nghĩa là phần còn lại của các câu lệnh awk sẽ bị bỏ qua và dòng tiếp theo sẽ được xem xét.

  • /R2/{print "*" $0;next}: Điều này có nghĩa là các dòng khớp với pattern /R2/hành động in *sẽ được thực hiện. Khi awkquá trình xử lý bắt đầu, pattern {action}câu lệnh đầu tiên sẽ bị bỏ qua vì điều pattern /R1/này sẽ không đúng với các dòng có /R2/. Vì vậy, pattern {action}tuyên bố thứ hai sẽ được thực hiện trên dòng. nextmột lần nữa có nghĩa là chúng tôi không muốn xử lý nữa và awksẽ chuyển sang dòng tiếp theo.

  • 1in tất cả các dòng. Khi chỉ cần một điều kiện được cung cấp không {action}, awk mặc định sử dụng {print}. Ở đây điều kiện được 1hiểu là đúng, vì vậy nó luôn thành công. Nếu chúng ta đi đến điểm này, đó là vì các pattern {action}câu lệnh thứ nhất và thứ hai đã bị bỏ qua hoặc bỏ qua (đối với các dòng không chứa /R1//R2/), do đó, hành động in mặc định sẽ được thực hiện cho các dòng còn lại.


Có vẻ để chạy nhanh nhất trong số tất cả các giải pháp được đăng.
Chris Xuống

1
Tôi không chắc đường cú pháp là thuật ngữ đúng ở đây ... Đó chỉ là cú pháp.
Daniel Hershcovich

7

awkthực hiện các nghi phạm thông thường khi nói đến điều kiện. Đó là một ý tưởng tốt để sử dụng printfthay vì printcho công việc mà bạn muốn làm phù hợp.

awk '{ if (/^R1/) { printf("=> %s\n", $0) } else if (/^R2/) { printf("* %s\n", $0) } else { print $0 } }'

Bạn không thực sự cần if-then-elseđiều này.
jaypal singh

1
Trong khi điều này hoạt động hoàn toàn tốt, nó không phải là thành ngữ. Việc sử dụng khôn ngoan nextlà một công cụ quan trọng trong lập trình awk.
dmckee

2
Tôi không hiểu quan điểm của việc sử dụng printfở đây. Ưu điểm duy nhất của nó (trừ khi bạn thực hiện định dạng fancier hơn so với ghép nối) là nó không thêm một dòng mới, không liên quan ở đây.
Gilles 'SO- ngừng trở nên xấu xa'

1
Đó là một kết quả trái ngược và đáng ngạc nhiên. Không được cung cấp printchỉ phải xuất $0trong khi printfphải phân tích một chuỗi định dạng.
jw013

5

Chris Down đã chỉ ra làm thế nào bạn có thể có được một biểu thức khác cho biểu thức chính quy bằng cách sử dụng câu lệnh 'if' rõ ràng trong một khối. Bạn cũng có thể nhận được hiệu quả tương tự theo một số cách khác, mặc dù giải pháp của anh ấy có lẽ tốt hơn.

Một là viết một biểu thức chính thứ ba sẽ chỉ khớp với văn bản không khớp với những người khác, trong trường hợp của bạn, điều này sẽ trông giống như thế này:

awk '/^R1/ { print "=>" $0}
     /^R2/ { print "*" $0}
     /^[^R]/ || /^R[^12]/ { print $0 } '

Lưu ý, điều này sử dụng regexps neo - ^ ở đầu regexps sẽ chỉ khớp ở đầu dòng - các mẫu ban đầu của bạn không làm điều này, điều này làm chậm sự khớp một chút vì nó sẽ kiểm tra tất cả các ký tự trên một dòng thay vì bỏ qua cho đến dòng tiếp theo Trường hợp thứ ba ("khác") sẽ khớp với một dòng bắt đầu bằng một số ký tự không phải là 'R' ([^ R]) hoặc bắt đầu bằng 'R' theo sau là một ký tự không phải là '1' hoặc ' 2 '(R [^ 12]). Hai ý nghĩa khác nhau của ^ có phần khó hiểu, nhưng sai lầm đó đã được thực hiện từ lâu và sẽ không được thay đổi bất cứ lúc nào sớm.

Để sử dụng các biểu thức bổ sung, chúng thực sự cần phải được neo, vì nếu không thì [^ R] sẽ khớp với ví dụ 1 theo sau nó. Đối với các biểu thức chính đơn giản như bạn có, cách tiếp cận này có thể hữu ích, nhưng khi các biểu thức chính trở nên phức tạp hơn, cách tiếp cận này sẽ trở nên khó kiểm soát. Thay vào đó, bạn có thể sử dụng các biến trạng thái cho mỗi dòng, như thế này:

awk '{ handled = 0 }
     /^R1/ { print "=>" $0; handled = 1}
     /^R2/ { print "*" $0; handled = 1}
     { if (!handled) print $0 } '

Bộ này được xử lý về 0 cho mỗi dòng mới, sau đó thành 1 nếu nó khớp với một trong hai biểu thức chính, và cuối cùng, nếu nó vẫn bằng 0, thực hiện in $ 0.


Cần lưu ý rằng trên các tệp lớn cả hai đều kém hiệu quả hơn so với sử dụng các điều kiện (như được hiển thị ở đây ). rfilechỉ là 10000 dòng dữ liệu của người hỏi lặp đi lặp lại.
Chris Xuống

4
if (!handled)Kinh quá! Sử dụng nextđể ngừng xem xét các hành động khác.
dmckee

+1 cho if (!handled). Các giải pháp chung, linh hoạt, có thể tái sử dụng là tốt. Điều gì xảy ra nếu người tiếp theo có câu hỏi này muốn xử lý nhiều hơn sau khi in? Các câu trả lời nextkhông hỗ trợ điều đó.
Scott
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.