AWK: Các chương trình mẫu. Chức năng cấp hệ thống. Ví dụ về lệnh AWK

Giới thiệu về ngôn ngữ tuyệt vời với tên lạ

Chuỗi nội dung:

Để bảo vệ awk

Trong loạt bài viết này, tôi sẽ giúp người đọc trở thành một lập trình viên awk lành nghề. Tôi đồng ý rằng awk không phải là điều dễ chịu nhất và tên thời trang và phiên bản GNU của awk, được gọi là gawk, nghe có vẻ hết sức lạ lùng. Các lập trình viên không quen với ngôn ngữ này khi nghe tên của nó có thể tưởng tượng ra một mớ mã cổ xưa và lỗi thời có thể khiến cả những người khó tính nhất cũng phát điên. chuyên gia am hiểu trên UNIX (khiến anh ta kêu lên "kill -9!" và liên tục chạy đi uống cà phê).

Vâng, awk thì không cái tên tuyệt vời. Nhưng đó là một ngôn ngữ tuyệt vời. Awk được thiết kế để xử lý văn bản và báo cáo, nhưng nó có nhiều tính năng được phát triển tốt cho phép lập trình nghiêm túc. Tuy nhiên, không giống như một số ngôn ngữ khác, cú pháp của awk quen thuộc và mượn những gì tốt nhất từ ​​các ngôn ngữ như C, python và bash (mặc dù chính thức awk đã được tạo trước python và bash). Awk là một trong những ngôn ngữ mà một khi đã học được sẽ trở thành một phần quan trọng trong kho vũ khí chiến lược của lập trình viên.

Bước đầu tiên trong awk

Hãy bắt đầu và thử trải nghiệm với awk để xem nó hoạt động như thế nào nhé. Tại dòng lệnh, nhập lệnh sau:

$awk "( print )" /etc/passwd

Kết quả sẽ hiển thị nội dung của tệp /etc/passwd. Bây giờ - giải thích về những gì awk đã làm. Khi gọi awk, chúng tôi đã chỉ định /etc/passwd làm tệp đầu vào. Khi chúng tôi chạy awk, nó xử lý lệnh in cho từng dòng trong /etc/passwd theo thứ tự. Tất cả đầu ra đã được gửi đến thiết bị xuất chuẩn và chúng tôi nhận được kết quả giống hệt với kết quả lệnh mèo/etc/passwd. Bây giờ hãy giải thích khối (in). Trong awk, dấu ngoặc nhọn được sử dụng để nhóm các khối văn bản, giống như trong C. Trong khối văn bản của chúng ta, chỉ có một lệnh in. Trong awk lệnh in không có thông số bổ sung in tất cả nội dung dòng hiện tại.

Đây là một ví dụ khác về chương trình awk thực hiện điều tương tự:

$awk "( print $0 )" /etc/passwd

Trong awk, biến $0 đại diện cho toàn bộ dòng hiện tại, vì vậy print và print $0 thực hiện chính xác điều tương tự. Nếu muốn, bạn có thể tạo một chương trình trong awk để xuất dữ liệu hoàn toàn không liên quan đến dữ liệu đầu vào. Đây là một ví dụ:

$awk "( print "" )" /etc/passwd

Khi bạn truyền chuỗi "" cho lệnh in, nó luôn in một chuỗi trống. Nếu bạn kiểm tra tập lệnh này, bạn sẽ thấy rằng awk xuất ra một dòng trống cho mỗi dòng trong /etc/passwd. Điều này một lần nữa xảy ra vì awk thực thi một tập lệnh cho mỗi dòng trong tệp đầu vào. Đây là một ví dụ khác:

$awk "( print "hiya")" /etc/passwd

Nếu bạn chạy tập lệnh này, nó sẽ lấp đầy màn hình với dòng chữ "yay". :)

Nhiều trường

Awk rất phù hợp để xử lý văn bản được chia thành nhiều trường logic và giúp bạn dễ dàng truy cập từng trường riêng lẻ từ bên trong tập lệnh awk. Đoạn script sau sẽ in danh sách tất cả các tài khoản trên hệ thống:

$ awk -F: "( print $1 )" /etc/passwd

Trong lệnh gọi awk trong ví dụ trên, tùy chọn –F chỉ định ://: dấu phân cách trường. Khi xử lý lệnh print $1, awk sẽ in trường đầu tiên gặp trên mỗi dòng của tệp đầu vào. Đây là một ví dụ khác:

$ awk -F: "( print $1 $3 )" /etc/passwd

Đây là một đoạn trích từ màn hình đầu ra của tập lệnh này:

dừng7 toán tử11 root0 tắt máy6 sync5 bin1 ....v.v.

Như bạn có thể thấy, awk xuất ra trường thứ nhất và thứ ba của tệp /etc/passwd, tương ứng là trường tên người dùng và uid. Đồng thời, mặc dù tập lệnh hoạt động nhưng nó không hoàn hảo - không có khoảng cách giữa hai trường đầu ra! Những người đã quen với việc lập trình bằng bash hoặc python có thể mong đợi lệnh print $1 $3 sẽ chèn khoảng trắng giữa hai trường này. Tuy nhiên, khi hai dòng nằm cạnh nhau trong chương trình awk, awk sẽ nối chúng mà không thêm khoảng trắng giữa chúng. Lệnh tiếp theo sẽ chèn một khoảng trắng giữa các trường:

$ awk -F: "( print $1 " " $3 )" /etc/passwd

Khi bản in được gọi theo cách này, nó nối $1 , " " và $3 thành chuỗi, tạo ra kết quả mà con người có thể đọc được trên màn hình. Tất nhiên, chúng ta cũng có thể chèn nhãn trường nếu cần:

$ awk -F: "( print "tên người dùng: " $1 "\t\tuid: " $3" )" /etc/passwd

Kết quả là, chúng tôi nhận được kết luận sau:

tên người dùng: dừng uid:7 tên người dùng: toán tử uid:11 tên người dùng: root uid:0 tên người dùng: tắt máy uid:6 tên người dùng: sync uid:5 tên người dùng: bin uid:1 ....

Tập lệnh bên ngoài

Truyền tập lệnh cho awk làm đối số dòng lệnh có thể thuận tiện cho các văn bản một dòng nhỏ, nhưng khi nói đến các chương trình nhiều dòng phức tạp, chắc chắn sẽ tốt hơn nếu viết tập lệnh dưới dạng tập tin bên ngoài. Sau đó, bạn có thể trỏ awk tới tệp tập lệnh này bằng tùy chọn -f:

$ awk -f myscript.awk myfile.in

Việc đặt các tập lệnh vào các tệp văn bản riêng biệt cũng cho phép bạn tận dụng lợi ích kèm theoôi. Ví dụ: tập lệnh nhiều dòng sau đây thực hiện tương tự như một trong các tập lệnh một dòng trước đó của chúng tôi - in trường đầu tiên của mỗi dòng từ /etc/passwd:

BẮT ĐẦU ( FS=:// ) ( in $1 )

Sự khác biệt giữa hai phương pháp này là cách chúng tôi chỉ định dấu phân cách trường. Trong tập lệnh này, dấu phân cách trường được chính chương trình chỉ định nội bộ (bằng cách đặt biến FS), trong khi ở ví dụ trước của chúng tôi, FS được định cấu hình bằng cách chuyển awk tùy chọn -F: // trên dòng lệnh. Thông thường, tốt nhất bạn nên chỉ định dấu phân cách trường trong chính tập lệnh, đơn giản vì nó sẽ không yêu cầu bạn phải nhớ một đối số dòng lệnh khác. Chúng ta sẽ xem xét biến FS chi tiết hơn ở phần sau của bài viết này.

Khối BEGIN và END

Thông thường, awk thực thi mỗi khối trong văn bản tập lệnh một lần cho mỗi dòng đầu vào. Tuy nhiên, thường có những tình huống trong lập trình mà bạn cần thực thi mã khởi tạo trước khi awk bắt đầu xử lý văn bản từ tệp đầu vào. Đối với những trường hợp như vậy, awk cung cấp khả năng xác định khối BEGIN. Chúng ta đã sử dụng khối BEGIN trong ví dụ trước. Vì khối BEGIN được xử lý trước khi awk bắt đầu xử lý tệp đầu vào, nên điều này nơi hoàn hảođể khởi tạo biến FS (Dấu tách trường), xuất tiêu đề hoặc khởi tạo các biến toàn cục khác sẽ được sử dụng sau này trong chương trình.

Awk cũng cung cấp một khối đặc biệt khác gọi là khối END. Awk thực thi khối này sau khi tất cả các dòng trong tệp đầu vào đã được xử lý. Thông thường, khối END được sử dụng để thực hiện các phép tính cuối cùng hoặc kết quả đầu ra sẽ xuất hiện ở cuối luồng đầu ra.

Biểu thức và khối thông thường

Awk cho phép bạn sử dụng các biểu thức chính quy để thực thi có chọn lọc các khối riêng lẻ của chương trình tùy thuộc vào việc kết quả có khớp hay không biểu hiện thông thường với dòng hiện tại. Đây là một tập lệnh mẫu chỉ in những dòng chứa chuỗi ký tự foo:

/foo/ (in)

Tất nhiên, bạn có thể sử dụng các biểu thức chính quy phức tạp hơn. Đây là tập lệnh sẽ chỉ xuất ra các dòng có chứa dấu phẩy:

/+\.*/ ( in )

Biểu thức và khối

Có nhiều cách khác để thực thi có chọn lọc một khối chương trình. Chúng ta có thể đặt bất kỳ biểu thức Boolean nào trước khối chương trình để điều khiển việc thực thi khối đó. Awk sẽ chỉ thực thi một khối chương trình nếu biểu thức Boolean trước đó có giá trị đúng. Tập lệnh ví dụ sau sẽ xuất ra trường thứ ba của tất cả các dòng trong đó trường đầu tiên là fred . Nếu trường đầu tiên của dòng hiện tại không phải là fred , awk sẽ tiếp tục xử lý tệp và sẽ không đưa ra câu lệnh in cho dòng hiện tại: :

$1 == "fred" ( in $3 )

Ưu đãi của Awk trọn bộ các toán tử so sánh, bao gồm các toán tử "==", " thông thường<", ">", "<=", ">=" và "!=". Ngoài ra, awk còn cung cấp các toán tử "~" và "!~", có nghĩa là "khớp" và "không khớp". Chúng đặt biến ở bên trái toán tử và biểu thức chính quy ở bên phải của nó. Đây là một ví dụ trong đó chỉ trường thứ ba của một dòng được in nếu trường thứ năm của cùng dòng chứa gốc của chuỗi ký tự:

$5 ~ /root/ ( in $3 )

Câu điều kiện

Awk cũng cung cấp các câu lệnh if giống C rất hay. Nếu muốn, bạn có thể viết lại tập lệnh trước đó bằng cách sử dụng if:

( if ($5 ~ /root/) ( in $3 ) )

Cả hai tập lệnh đều hoạt động giống hệt nhau. Trong ví dụ đầu tiên, biểu thức boolean nằm ngoài khối, trong khi ở ví dụ thứ hai, khối được thực thi cho mỗi dòng đầu vào và chúng ta thực thi có chọn lọc lệnh in bằng câu lệnh if. Cả hai phương thức đều hoạt động và chúng ta có thể chọn một phương thức. cái đó cách tốt nhất hợp nhất với các phần khác của tập lệnh.

Đây là nhiều hơn nữa ví dụ phức tạp câu lệnh if trong awk. Như bạn có thể thấy, ngay cả với các điều kiện lồng nhau phức tạp, các câu lệnh if trông giống hệt với câu lệnh C của chúng:

( if ($1 == "foo") ( if ($2 == "foo") ( print "uno" ) else ( print "one" ) ) else if ($1 == "bar") ( print "two" ) khác ( in "ba") )

Sử dụng câu lệnh if, chúng ta có thể chuyển đổi mã này:

! /matchme/ ( in $1 $3 $4 )( if ($0 !~ /matchme/) ( in $1 $3 $4 ) )

Cả hai tập lệnh sẽ chỉ in những dòng Không chứa chuỗi ký tự matchme . Và trong trường hợp này cũng vậy, bạn có thể chọn một phương pháp hoạt động tốt hơn trong một chương trình cụ thể. Cả hai đều làm điều tương tự.

Awk cũng cung cấp cho bạn khả năng sử dụng toán tử Boolean "||" (“logic OR”) và “&&” (“logic AND”), cho phép bạn tạo các biểu thức Boolean phức tạp hơn:

($1 == "foo") && ($2 == "bar") ( print )

Ví dụ này sẽ chỉ xuất ra các dòng có trường đầu tiên là foo trường thứ hai là bar .

Biến số!

Cho đến nay chúng tôi đã in các biến chuỗi, toàn bộ chuỗi hoặc các trường cụ thể. Tuy nhiên, awk cũng cho chúng ta khả năng thực hiện so sánh trên cả số nguyên và số dấu phẩy động. Sử dụng các biểu thức toán học, rất dễ dàng để viết một tập lệnh đếm một số dòng trống trong tập tin. Đây là một kịch bản như vậy:

BEGIN ( x=0 ) /^$/ ( x=x+1 ) END ( in "Đã tìm thấy " x " dòng trống. :))" )

Trong khối BEGIN, chúng ta khởi tạo biến số nguyên x về 0. Sau đó, mỗi khi awk gặp một dòng trống, nó sẽ thực thi câu lệnh x=x+1 , tăng x thêm 1. Khi tất cả các dòng đã được xử lý, khối END sẽ được thực thi và awk sẽ in tổng số cuối cùng, cho biết số dòng trống được tìm thấy.

Biến chuỗi

Một trong những điều thú vị về các biến awk là chúng là "chữ thường và chữ thường". Tôi gọi các biến awk là "chuỗi" vì tất cả các biến awk đều được lưu trữ nội bộ dưới dạng chuỗi. Đồng thời, các biến awk rất "đơn giản" vì bạn có thể thực hiện phép toán trên một biến và nếu nó chứa một chuỗi số hợp lệ, awk sẽ tự động đảm nhiệm việc chuyển đổi chuỗi đó thành một số. Để hiểu ý tôi, hãy xem ví dụ này:

x="1.01" # Chúng tôi đã tạo x chứa *string* "1.01" x=x+1 # Chúng tôi vừa thêm 1 vào *string* print x # Đây chỉ là một nhận xét :)

Awk sẽ xuất ra:

2.01

Tò mò! Mặc dù chúng tôi đã gán biến x Chuỗi giá trị 1.01, chúng tôi vẫn có thể thêm một cái vào đó. Chúng tôi không thể làm điều này trong bash hoặc python. Trước hết, bash không hỗ trợ số học dấu phẩy động. Và mặc dù bash có các biến "chuỗi", nhưng chúng không "đơn giản"; Để thực hiện bất kỳ phép toán nào, bash yêu cầu chúng ta gói các phép tính của mình trong các cấu trúc $() xấu xí. Nếu chúng ta đang sử dụng python, chúng ta sẽ cần chuyển đổi rõ ràng chuỗi 1.01 thành giá trị dấu phẩy động trước khi thực hiện bất kỳ phép tính nào với nó. Tuy không khó nhưng vẫn bước bổ sung. Trong trường hợp awk, tất cả điều này được thực hiện tự động và nó làm cho mã của chúng tôi đẹp và rõ ràng. Nếu cần bình phương trường đầu tiên của mỗi chuỗi đầu vào và thêm một trường vào đó, chúng ta sẽ sử dụng tập lệnh như sau:

( in ($1^2)+1 )

Nếu thử nghiệm một chút, bạn sẽ thấy rằng nếu một biến không chứa số hợp lệ, awk sẽ coi biến đó là số 0 khi đánh giá một biểu thức toán học.

Nhiều nhà khai thác

Một tính năng thú vị khác của awk là bộ toán tử hoàn chỉnh. Ngoài phép cộng, trừ, nhân và chia tiêu chuẩn, awk còn cho chúng ta khả năng sử dụng toán tử số mũ đã được trình bày trước đó "^", toán tử số dư chia số nguyên "%" và nhiều toán tử gán thuận tiện khác mượn từ C.

Chúng bao gồm các toán tử gán trước và sau tăng/giảm (i++, --foo), các toán tử gán với phép cộng/trừ/nhân/chia (a+=3, b*=2, c/=2.2, d-=6.2) . Nhưng đó không phải là tất cả - chúng tôi còn có các toán tử gán thuận tiện để tính phần còn lại của phép chia số nguyên và lũy thừa (a^=2, b%=4).

Dấu phân cách trường

awk có cái riêng của nó bộ riêng các biến đặc biệt. Một số trong số đó làm cho nó có thể tinh chỉnh awk hoạt động và những cái khác chứa thông tin có giá trị về đầu vào. Chúng ta đã đề cập đến một trong những biến đặc biệt này, FS. Như đã đề cập trước đó, biến này cho phép bạn chỉ định chuỗi ký tự mà awk sẽ coi là dấu phân cách trường. Khi chúng tôi sử dụng /etc/passwd làm đầu vào, FS được đặt thành ://. Điều này hóa ra là đủ, nhưng FS còn mang lại cho chúng tôi sự linh hoạt hơn nữa.

Giá trị của biến FS không nhất thiết phải là một ký tự đơn; nó có thể được gán một biểu thức chính quy chỉ định mẫu ký tự có độ dài bất kỳ. Nếu bạn đang xử lý các trường được phân tách bằng một hoặc nhiều ký tự tab thì FS phải được định cấu hình như sau:

FS="\t+"

Ở trên chúng tôi đã sử dụng tính cách đặc biệt biểu thức chính quy "+" có nghĩa là "một hoặc nhiều lần xuất hiện của ký tự trước".

Nếu các trường được phân tách bằng khoảng trắng (một hoặc nhiều dấu cách hoặc tab), bạn có thể muốn đặt FS thành biểu thức chính quy sau:

FS="[[:dấu cách:]+]"

Mặc dù thiết lập này sẽ hoạt động nhưng nó không cần thiết. Tại sao? Bởi vì giá trị mặc định của FS là một ký tự khoảng trắng, được awk hiểu là "một hoặc nhiều khoảng trắng hoặc tab". Trong của chúng tôi ví dụ cụ thể Giá trị FS mặc định chính xác là những gì chúng ta cần!

Cũng không có vấn đề gì với các biểu thức chính quy phức tạp. Ngay cả khi các bản ghi được phân tách bằng từ "foo" theo sau là ba chữ số, biểu thức chính quy sau đây sẽ phân tích dữ liệu một cách chính xác:

FS="foo"

Số lượng trường

Hai biến tiếp theo mà chúng ta sẽ xem xét thường không được dùng để ghi vào mà được sử dụng để đọc và lấy dữ liệu. thông tin hữu ích về đầu vào. Đầu tiên trong số này là biến NF, còn được gọi là số lượng trường. Awk tự động đặt giá trị của biến này bằng số các trường trong bản ghi hiện tại. Bạn có thể sử dụng biến NF để chỉ hiển thị một số dòng đầu vào nhất định:

NF == 3 ( print "có ba trường trong mục này: " $0 )

Tất nhiên, biến NF cũng có thể được sử dụng trong câu điều kiện, Ví dụ:

( if (NF > 2) ( print $1 " " $2 ://: // $3 ) )

Con số kỷ lục

Một biến thuận tiện khác là số bản ghi (NR). Nó luôn chứa số của bản ghi hiện tại (awk coi bản ghi đầu tiên là bản ghi số 1). Cho đến nay chúng ta đã xử lý các tệp đầu vào chứa một bản ghi trên mỗi dòng. Trong những tình huống như vậy, NR cũng sẽ báo cáo số dòng hiện tại. Tuy nhiên, khi chúng ta bắt đầu xử lý các bản ghi nhiều dòng trong các bài viết sau của loạt bài này, điều này sẽ không còn xảy ra nữa, vì vậy bạn cần phải cẩn thận! NR chỉ có thể được sử dụng giống như biến NF cho đầu ra một số dòng nhất địnhđầu vào:

(NR< 10) || (NR >100) ( in "Chúng tôi đang ở số kỷ lục 1-9 hoặc 101 trở lên") )

Một ví dụ nữa:

( #skip tiêu đề if (NR > 10) ( print "bây giờ đã có thông tin thực!" ) )

Awk cung cấp các biến bổ sung có thể được sử dụng cho nhiều mục đích khác nhau. Chúng ta sẽ xem xét các biến này trong các bài viết sau. Chúng ta đã kết thúc quá trình khám phá ban đầu về awk. Trong các bài viết tiếp theo của loạt bài này, tôi sẽ trình bày chức năng awk nâng cao hơn và chúng tôi sẽ kết thúc loạt bài này bằng một ứng dụng awk trong thế giới thực. Trong thời gian chờ đợi, nếu muốn tìm hiểu thêm, bạn có thể xem các tài nguyên được liệt kê bên dưới.

Trong bài viết này chúng tôi sẽ giới thiệu cho bạn một số ví dụ thực tế về cách sử dụng AWK trên .

Giới thiệu

AWK được đặt theo tên của các tác giả: Alfred Aho, Peter Weinberger và Brian Kernighan. AWK là một ngôn ngữ kịch bản rất hữu ích để xử lý văn bản. Ngôn ngữ này chạy trong một trình thông dịch. Điều này cho phép người dùng xử lý một số đầu vào, xác định các biến, sử dụng toán tử logic, hàm chuỗi và số, trích xuất dữ liệu và tạo báo cáo được định dạng. Cú pháp của AWK rất giống với ngôn ngữ C và là tiền thân trực tiếp của Perl. Tất cả các tập lệnh AWK có thể được chuyển đổi thành tập lệnh Perl bằng tiện ích A2P.

Điều kiện tiên quyết

Trình thông dịch AWK là công cụ tiêu chuẩn, được tìm thấy trên mọi bản phân phối Linux. Gói gawk chứa phiên bản mã nguồn mở của AWK mã nguồn, và tùy thuộc vào Phân phối Linux nó có thể được cài đặt từ tập tin nguồn hoặc sử dụng các gói gawk hoặc mawk đi kèm với bản phân phối Linux cụ thể của bạn.

Cài đặt

Với quyền siêu người dùng

Ssh root@IP_Address

Để cài đặt tiện ích dòng lệnh AWK trên /Fedora hoặc bất kỳ tiện ích dựa trên RPM nào khác Bản phân phối Linux, chạy lệnh sau:

Yum cài đặt gawk

Trong / , bạn cần gọi lệnh này để cài đặt Gawk:

Cài đặt apt-get gawk

Ví dụ về lệnh AWK

Các lệnh awk đơn giản có thể dễ dàng chạy từ dòng lệnh và hơn thế nữa nhiệm vụ phức tạp phải được viết dưới dạng tập lệnh awk vào một tệp. Dưới đây là một số ví dụ hữu ích lệnh awk và tập lệnh thực thi.

Bạn có thể sử dụng lệnh AWK để chỉ in các cột cụ thể từ trường đầu vào. Ví dụ: sử dụng lệnh bên dưới, bạn có thể tìm ra danh sách các địa chỉ IP được kết nối với máy chủ:

Netstat -anp|grep tcp|awk "(print $5)"| cắt -d: -f1 | sắp xếp | uniq -c | sắp xếp -n

Điều này rất hữu ích nếu bạn đang điều tra xem liệu máy chủ của bạn có bị ảnh hưởng hay không. Tấn công vào hệ điều hành Dos hoặc DDoS.

Trong ví dụ sau, chúng tôi sử dụng AWK để tìm kiếm một mẫu cụ thể trong các cột nhất định và thực hiện một số hành động dựa trên kết quả:

Exim -bpr | grep đông lạnh | awk("in $3") | xargs exim -Mrm

Lệnh trên sẽ xóa tất cả các tin nhắn bị đóng băng E-mail từ hàng đợi thư Exim.

AWK thường được sử dụng để thực hiện các hoạt động hữu ích và xử lý thực tế và thao tác văn bản. Ví dụ: chúng ta có thể sử dụng AWK để xóa các bản sao trong tệp văn bản mà không cần sắp xếp:

Awk "!x[$0]++" tệp có bản sao > tệp mới không trùng lặp

Lệnh sau sẽ in năm Số ngẫu nhiên từ 0 đến 999:

Ồ "BẮT ĐẦU ( for (i = 1; i<= 5; i++) print int(1000 * rand()) }"

Sử dụng lệnh sau để đếm số dòng trong tệp có tên "sample_file":

Awk "END ( print NR )" sample_file

Lệnh sau sẽ in tất cả các dòng trong tệp "sample_file" chứa các dòng bắt đầu bằng 'A' hoặc 'a' theo sau là 're':

Ôi "/re/(print)" /opt/sample_file

Bạn có thể sử dụng lệnh AWK cho các thao tác phức tạp hơn. Nếu trang web của bạn chạy khá chậm, bạn có thể sử dụng lệnh sau để kiểm tra xem có vấn đề gì với đĩa I/O (và/hoặc mạng, trong một số trường hợp hiếm gặp):

Tac /proc/stat | awk "/^btime/ (up=systime()-$2;print "up " up/86400 "d"); /^cpu / (print "user " $2/up "%, đẹp " $3/up "%, sys " $4/up "%, không hoạt động " $5/up "%, iowait " $6/up "%, ăn trộm " $9/up "%\niowait/used " $6 / ($2+$3+$4) ", ăn trộm/đã sử dụng "$9 / ($2+$3+$4) )"

IOWAIT có nghĩa là các quá trình bị chặn trong bao lâu khi bận rộn với I/O, chủ yếu là lưu trữ đĩa hoặc có thể là mạng. STEAL có nghĩa là các tiến trình bị CPU chặn trong bao lâu Thời gian cắt may mắn trên máy chủ. iowait ở trên cho thời gian CPU của người dùng (=USER + NICE + SYSTEM) hiển thị I/O bận, hành vi đánh cắp ở trên hiển thị CPU đang bận.

Tập lệnh sau sử dụng lệnh awk đơn giản để tìm kiếm tệp đầu vào '/etc/passwd' và cung cấp đầu ra với tên người dùng theo sau là ngày và giờ của lần đăng nhập cuối cùng:

Vi đăng nhập-kiểm tra #!/bin/bash cho người dùng trong `awk -F: "(print $1)" /etc/passwd` do echo -n "$user: " Finger $user | grep Cuối cùng nếu [ $? != 0 ]; sau đó echo fi xong

Làm cho tập lệnh có thể thực thi được:

Kiểm tra đăng nhập Chmod 755

Thực thi kịch bản:

./đăng nhập-kiểm tra

Bạn sẽ có thể xem các tài khoản người dùng có sẵn trên máy chủ, sau đó là ngày và giờ đăng nhập lần cuối của mỗi người dùng.

Phần kết luận

Có một số ngôn ngữ mới như Perl và Python có thể được sử dụng thay cho AWK, nhưng sử dụng AWK có một số ưu điểm như:

  • AWK rất dễ nhận biết.
  • AWK có thể được sử dụng để giải quyết một số loại vấn đề nhất định nhanh hơn và tạo tập lệnh hiệu quả hơn so với sử dụng các công cụ/ngôn ngữ khác.
  • AWK rất hữu ích khi làm việc với các tệp lớn như nhật ký, v.v. vì với sự trợ giúp của lệnh/tập lệnh AWK, bạn có thể tạo báo cáo được lọc và dễ đọc.

Mục đích chính của chương trình ôi- giải thích ngôn ngữ lập trình awk, cho phép bạn nhanh chóng tạo một chương trình để phân tích và chuyển đổi các tệp văn bản. Một tập lệnh awk điển hình trông như thế này:

mẫu1 (hành động1) mẫu2 (hành động2) ...

Chương trình đọc tệp hoặc luồng đầu vào và phân tích từng dòng để tìm mẫu khớp. Nếu trùng khớp, các hành động được mô tả trong dấu ngoặc nhọn sẽ được thực hiện. Nếu mẫu bị thiếu, hành động sẽ được áp dụng cho từng hàng. Nếu không có hành động nào, dòng này sẽ được in ra đầu ra tiêu chuẩn. Một hành động có thể bao gồm một chuỗi các câu lệnh được phân tách bằng dấu chấm phẩy.

In trường thứ ba (cột, từ) của mỗi dòng:

Mã số:

ls -l | ôi "(in($3))"

In hai trường được chỉ định của mỗi dòng:

Mã số:

ls -l | awk "(print($9,$1))"

Các trường in có dấu cách:

Mã số:

ls -l | awk "(print($9," ",$1))"

Để chỉ định dấu tách trường không phải dấu cách, hãy sử dụng tùy chọn -F. Trong trường hợp này, dấu phân cách trường sẽ là dấu hai chấm:

Mã số:

Awk -F: "(print($2))" $filenameForProcessing

Để sử dụng tập lệnh awk được lưu trong một tệp:

Mã số:

Ôi -f $scriptFilename $filenameForProcessing

Tệp tập lệnh awk có thể được thực thi và sha-bang tương ứng được chỉ định trong đó. Tập lệnh như vậy sẽ lấy một tệp để xử lý làm tham số:

Mã số:

#!/usr/bin/awk -f

Các biến awk được tạo trong lần truy cập đầu tiên và có thể chứa số nguyên, số float hoặc chuỗi, tùy theo ngữ cảnh xác định. Biến đặc biệt RS lưu trữ giá trị của dấu phân cách bản ghi (\n theo mặc định) và biến FS lưu giá trị của dấu phân cách trường (mặc định - dấu cách). Nếu bất kỳ biến nào trong số này chứa nhiều hơn một ký tự, giá trị đó sẽ được hiểu là biểu thức chính quy. Ngôn ngữ awk chứa một số hàm toán học và chuỗi tích hợp, câu lệnh điều kiện và câu lệnh lặp, hỗ trợ mảng và xác định các hàm do người dùng xác định. Trên Internet, bạn có thể tìm thấy các hướng dẫn sử dụng rộng rãi về ngôn ngữ awk, cũng như các trình dịch tự động (“trình dịch”) các tập lệnh awk sang các ngôn ngữ khác (ví dụ: C hoặc Perl).

Các loại mẫu đặc biệt là BEGIN và END. Chúng không được kiểm tra đối với các mục nhập luồng đầu vào. Hành động mẫu BEGIN sẽ được thực thi một lần trước khi dữ liệu đầu vào được đọc và hành động mẫu KẾT THÚC sẽ được thực thi một lần sau khi dữ liệu đầu vào được đọc.

Một ví dụ về loại bỏ các dòng trùng lặp liền kề trong một tệp:

Mã số:

Tên tệp=test.txt
res=`awk "BEGIN (PREV="") (if ($0 != PREV) (in $0; PREV=$0))" $filename`
echo "$res" > tên tệp $

Trước khi bắt đầu, chúng ta đặt biến PREV thành chuỗi trống. Phần còn lại của tập lệnh awk không có mẫu và do đó được thực thi trên bất kỳ chuỗi nào. Nếu dòng hiện tại không phải là PREV, nó sẽ được in và ghi vào biến PREV. Như vậy, khi xử lý từng dòng nếu bằng dòng trước thì sẽ không in được.

Ví dụ về nối trường:

Mã số:

Ồ "(a = $3 $4; in a)" $filename

Ví dụ về các giá trị trường tổng hợp:

Mã số:

Ồ "(a = $3+$4; in a)" $filename

Khái niệm "bộ chọn" nên được hiểu là phần mở rộng của khái niệm "mẫu". Nói chung, khi một mẫu được chỉ định trong cấu trúc lệnh, bất kỳ bộ chọn nào cũng có thể xuất hiện.

Kiểm tra trường thứ ba dựa trên biểu thức chính quy và in toàn bộ dòng nếu thành công:

Mã số:

Ồ "$3~/(7)$/ (print)" $filename

Kiểm tra sự bằng nhau của trường đầu tiên với chuỗi đã chỉ định và in trường thứ hai nếu thành công.

04.10.2015
16:55

Tiện ích awk là một ví dụ về ứng dụng xử lý văn bản Linux cổ điển. Nó rất linh hoạt và hiệu quả, mặc dù nó không cung cấp ngôn ngữ lập trình chính thức. Tuy nhiên, hãy yên tâm rằng khả năng của nó khá đủ để giải quyết nhiều tác vụ xử lý văn bản tự động (đặc biệt khi kết hợp với các tiện ích console khác).

Cách chạy chương trình awk

Nếu chương trình awk khá đơn giản và ngắn, thì mã của nó có thể được nhập trực tiếp vào bảng điều khiển:

Ối"< код awk-программы >" < имя_файла_для_обработки >

Bạn có thể sử dụng nhiều thứ hơn là chỉ awk làm đầu vào: tập tin văn bản, mà còn xuất ra luồng tiêu chuẩn của các ứng dụng khác:

< некое_приложение >| ôi"< код awk-программы >"

Trong trường hợp mã chương trình awk khá lớn hoặc phải lưu lại để sử dụng tái sử dụng, nó có thể được gọi từ một tệp có khóa chuyển đổi -f:

Ối -f< имя_файла_с_кодом_awk_программы > < имя_файла_для_обработки >

Để thực hiện các thử nghiệm, chúng tôi sử dụng tệp test.cpp, trên đó chúng tôi sẽ kiểm tra kết quả của các chương trình awk:

#bao gồm #bao gồm #bao gồm void test1(); int test2(); // Chú thích kiểu C cho hàm main() int main(int argc, char** argv) ( std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { std::cout << i << std::endl; } return 0; } // Комментарий в стиле С для функции test1() void test1() { std::cout << "Hello, test1!" << std::endl; } // Комментарий в стиле С для функции test2() int test2() { std::cout << "Hello, test2!" << std::endl; }

Quảng cáo

Lọc chuỗi bằng awk

Trước hết, awk cho phép bạn chọn các dòng từ văn bản dựa trên các biểu thức thông thường và một số điều kiện số.

Chọn các chuỗi khớp với biểu thức chính quy

Ví dụ: để lấy tất cả các dòng trong tệp test.cpp chứa chỉ thị tiền xử lý #include, chúng tôi sử dụng lệnh sau:

Ôi "/^#\s*include/" test.cpp

Biểu thức chính quy được viết giữa hai ký tự /. Kết quả là chúng tôi nhận được:

#bao gồm #bao gồm #bao gồm

Chọn các chuỗi KHÔNG khớp với biểu thức chính quy

Để loại bỏ tất cả các dòng không khớp với biểu thức chính quy, hãy sử dụng lệnh từ phần phụ trước và thêm dấu chấm than vào đầu mã awk. Ví dụ: theo cách này chúng tôi sẽ loại trừ tất cả các dòng nhận xét:

Ôi "! /^[/](2).*/" test.cpp

Đây là những gì còn lại:

#bao gồm #bao gồm #bao gồm void test1(); int test2(); int main(int argc, char** argv) ( std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { std::cout << i << std::endl; } return 0; } void test1() { std::cout << "Hello, test1!" << std::endl; } int test2() { std::cout << "Hello, test2!" << std::endl; }

Chọn các hàng từ một phạm vi nhất định

Bạn có thể xác định một phạm vi chuỗi sẽ hiển thị trên màn hình bằng hai biểu thức thông thường, được phân tách bằng dấu phẩy. Ví dụ: hãy tìm định nghĩa của tất cả các hàm trả về int:

Ôi "/^int .*(.*) (/, /^)/" test.cpp

Kết quả liên quan:

Int main(int argc, char** argv) ( std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { std::cout << i << std::endl; } return 0; } int test2() { std::cout << "Hello, test2!" << std::endl; }

Kết hợp các điều kiện lọc

Để kiểm tra các chuỗi theo nhiều điều kiện cùng một lúc, hãy sử dụng toán tử && (AND) và ||. (HOẶC) .

Lệnh sau in tất cả các bình luận không chứa main:

Ôi "/[/](2).*/ && ! /main/" test.cpp

Kết quả là chúng ta có:

// Chú thích kiểu C cho hàm test1() // Chú thích kiểu C cho hàm test2()

Trước đây, chúng tôi đã tìm kiếm một loạt các dòng bằng cách sử dụng hai biểu thức chính quy, nhưng nếu biết trước số dòng cần xuất ra thì mọi thứ sẽ được đơn giản hóa:

Ối "4< NR && NR < 7" test.cpp

NR là biến awk chỉ định số dòng. Do đó, mã được trình bày xuất ra dòng thứ 5 và thứ 6:

Kiểm tra trống1(); int test2();

Lựa chọn các dòng dựa trên các điều kiện liên quan đến từng từ riêng lẻ

Awk có thể lọc văn bản không chỉ theo dòng mà còn theo từng từ riêng lẻ. Từ thứ i trong một dòng có thể được tham chiếu bằng cách sử dụng $i . Việc đánh số bắt đầu từ một và $0 xác định nội dung của toàn bộ dòng. Số lượng từ trong một dòng được xác định bằng biến NF, do đó $NF trỏ đến từ cuối cùng. Ví dụ: hãy tìm các chuỗi có từ đầu tiên là int hoặc void:

Awk "$1 == "int" || $1 == "void"" test.cpp

Đầu ra giao diện điều khiển tương ứng:

Kiểm tra trống1(); int test2(); int main(int argc, char** argv) ( void test1() ( int test2() (

Tuy nhiên, việc sử dụng tính năng kiểm tra biểu thức chính quy đối với một từ sẽ dễ dàng hơn. Để làm điều này, awk cung cấp một toán tử đặc biệt ~, toán tử này phải được đặt giữa biến trỏ tới từ đó và biểu thức chính quy. Ví dụ: hãy viết lại lệnh trước đó ở dạng gọn hơn:

Awk "$1 ~ /int|void/" test.cpp

Chọn hàng dựa trên đặc điểm số

Các toán tử số học của ngôn ngữ C có sẵn trong awk, giúp bạn tự do hành động. Ví dụ bên dưới in tất cả các dòng chẵn (NR là số dòng):

Awk "NR % 2 == 0" test.cpp

Đầu ra có liên quan:

#bao gồm int test2(); // Chú thích kiểu C cho hàm main() std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { } return 0; void test1() { } // Комментарий в стиле С для функции test2() std::cout << "Hello, test2!" << std::endl;

Chương trình awk sau đây in tất cả các dòng có từ đầu tiên có độ dài bằng ba:

Awk "length($1) == 3" test.cpp

Kết quả là chúng tôi nhận được:

Int test2(); int main(int argc, char** argv) ( int test2() (

Ôi "NF == 2" test.cpp

Và đầu ra tương ứng:

#bao gồm #bao gồm #bao gồm void test1(); int test2(); trả về 0;

Quảng cáo

Làm việc với chuỗi trong awk

Như bạn có thể thấy, awk có một bộ hàm tốt để lọc chuỗi văn bản. Tuy nhiên, bạn vẫn có thể thực hiện nhiều phép biến đổi khác nhau trên các chuỗi này. Các lệnh chuỗi phải được gói trong dấu ngoặc nhọn (...). Mã trong ngoặc đơn được gọi tuần tự cho từng dòng văn bản đang được xử lý.

Đầu ra được định dạng

Awk có chức năng tương đương trực tiếp với hàm printf() của ngôn ngữ C. Ví dụ: hãy in số của nó ở đầu mỗi dòng:

Awk "( printf "%-2d %s\n", NR, $0 )" test.cpp

Đây là những gì chúng tôi có:

1 #bao gồm 2 #bao gồm 3 #bao gồm 4 5 void test1(); 6 int test2(); 7 8 // Chú thích kiểu C cho hàm main() 9 int main(int argc, char** argv) ( 10 std::cout<< "Hello, world!" << std::endl; 11 12 for(int i = 0; i < 10; ++i) { 13 std::cout << i << std::endl; 14 } 15 16 return 0; 17 } 18 19 // Комментарий в стиле С для функции test1() 20 void test1() { 21 std::cout << "Hello, test1!" << std::endl; 22 } 23 24 // Комментарий в стиле С для функции test2() 25 int test2() { 26 std::cout << "Hello, test2!" << std::endl; 27 }

Hàm chuyển đổi

Ngoài printf(), awk còn có các chức năng khác. Ví dụ: print() và toupper() :

Awk "( print toupper($0) )" test.cpp

Kết quả liên quan:

#BAO GỒM #BAO GỒM #BAO GỒM KIỂM TRA VOID1(); INT TEST2(); // BÌNH LUẬN C-STYLE CHO MAIN() FUNCTION INT MAIN(INT ARGC, CHAR** ARGV) ( STD::COUT<< "HELLO, WORLD!" << STD::ENDL; FOR(INT I = 0; I < 10; ++I) { STD::COUT << I << STD::ENDL; } RETURN 0; } // КОММЕНТАРИЙ В СТИЛЕ С ДЛЯ ФУНКЦИИ TEST1() VOID TEST1() { STD::COUT << "HELLO, TEST1!" << STD::ENDL; } // КОММЕНТАРИЙ В СТИЛЕ С ДЛЯ ФУНКЦИИ TEST2() INT TEST2() { STD::COUT << "HELLO, TEST2!" << STD::ENDL; }

Câu điều kiện

Câu lệnh if-else có sẵn trong các chương trình awk. Ví dụ: đoạn mã sau in mà không thay đổi các dòng có int ở vị trí đầu tiên và ( ở vị trí cuối cùng, nếu không thì --- được gửi tới bảng điều khiển:

Awk " ( if($1 == "int" && $NF == "() print; else print "---" )" test.cpp

Chạy mã sẽ tạo ra kết quả sau:

Int main(int argc, char** argv) ( --- --- --- --- --- --- --- --- --- --- --- --- -- - --- --- int test2() ( --- ---

Biến

Các biến không cần khai báo trước cũng có sẵn trong chương trình awk. Đoạn mã sau để đếm số dòng và số từ trong văn bản sẽ được đặt trong tệp stat.awk:

( lineCount++; wordCount += NF ) END ( printf "số dòng: %d, số từ: %d\n", lineCount, wordCount )

Sau đó, nó được gọi như sau:

Awk -f stat.awk test.cpp

Kết quả thực hiện:

Số dòng: 27, số từ: 88

Bộ lọc END chỉ định rằng mã trong dấu ngoặc đơn chỉ được thực thi sau khi tất cả các dòng đã được duyệt qua. Bộ lọc BEGIN cũng có sẵn trong awk, vì vậy trong trường hợp tổng quát hơn, chương trình có dạng:

BEGIN (Được gọi trước khi quá trình duyệt hàng bắt đầu) (Được gọi cho mỗi hàng sau phần BEGIN, nhưng trước phần END) END (Được gọi sau khi quá trình duyệt hàng hoàn tất)

Wc -lw test.cpp

Chu kỳ

Trong các chương trình awk, bạn cũng có quyền truy cập vào các vòng lặp for và while kiểu C. Ví dụ: hãy in tất cả các dòng theo thứ tự ngược lại. Hãy tạo một tệp Reverse.awk với nội dung sau:

( for(i = NF; i > 0; --i) printf "%s ", $i; printf "\n" )

Hãy gọi chương trình như sau:

Awk -f đảo ngược.awk test.cpp

Kết quả các từ ở mỗi dòng sẽ được in theo thứ tự ngược lại:

#bao gồm #bao gồm #include test1(); void test2(); int main() các hàm dành cho kiểu C trong Comment // () argv char** argc, int main(int std::endl;<< world!" "Hello, << std::cout {) ++i 10; < i 0; = i int for(std::endl; << i << std::cout } 0; return } test1() функции для С стиле в Комментарий // { test1() void std::endl; << test1!" "Hello, << std::cout } test2() функции для С стиле в Комментарий // { test2() int std::endl; << test2!" "Hello, << std::cout }

Dấu phân cách từ không chuẩn

Theo mặc định, awk sử dụng các ký tự khoảng trắng làm dấu phân cách từ, nhưng hành vi này có thể được thay đổi. Để thực hiện việc này, hãy sử dụng khóa chuyển -F, theo sau là dòng xác định dấu phân cách. Ví dụ: chương trình sau hiển thị tên của một nhóm và người dùng của nhóm đó (nếu nhóm có người dùng) từ tệp /etc/group, sử dụng ký tự dấu hai chấm làm dấu phân cách:

Awk -F: "( if($4) printf "%15s: %s\n", $1, $4 )" /etc/group

Kết hợp các bộ lọc và lệnh in

Tất cả các bộ lọc đã thảo luận trước đây có thể được sử dụng cùng với các lệnh xử lý chuỗi. Chỉ cần viết các hạn chế trước dấu ngoặc nhọn là đủ. Dưới đây là ví dụ để in 9 dòng đầu tiên của đầu ra của lệnh ps, chứa thông tin về người dùng, ID tiến trình và tên lệnh:

Ps axu | ôi "NR< 10 { print $1, $2, $NF }"

Sau khi ra mắt chúng ta sẽ thấy:

NGƯỜI DÙNG PID LỆNH root 1 /sbin/init root 2 root 3 root 5 root 7 root 8 root 9 root 10

Giới thiệu về một ngôn ngữ tuyệt vời với một cái tên lạ

Daniel Robbins, Chủ tịch/Giám đốc điều hành, Gentoo Technologies, Inc.

Mô tả: Awk là một ngôn ngữ tuyệt vời với cái tên rất lạ. Trong bài viết đầu tiên của loạt bài gồm ba phần này, Daniel Robbins giới thiệu ngắn gọn về kiến ​​thức cơ bản về lập trình với awk. Các bài viết tiếp theo trong loạt bài này sẽ đề cập đến các chủ đề nâng cao hơn, kết thúc bằng một ứng dụng demo thực tế, vững chắc.

Thẻ bài viết này: awk

Gắn cờ này!

Ngày: 29/01/2009

Mức độ khó: dễ

Bình luận: 0 (Xem | Thêm bình luận - Đăng nhập)

Đánh giá bài viết này

Để bảo vệ awk

Trong loạt bài viết này, tôi sẽ giúp người đọc trở thành một lập trình viên awk lành nghề. Tôi đồng ý rằng awk không có cái tên đẹp nhất hoặc thời thượng nhất và phiên bản GNU của awk, được gọi là gawk, nghe có vẻ hết sức kỳ lạ. Các lập trình viên không quen với ngôn ngữ này có thể nghe thấy tên của nó và tưởng tượng ra một mớ mã cổ xưa và lỗi thời có thể khiến ngay cả chuyên gia UNIX hiểu biết nhất cũng phát điên (khiến anh ta kêu lên "kill -9!" và liên tục chạy đi uống cà phê).

Vâng, awk không có một cái tên hay. Nhưng đó là một ngôn ngữ tuyệt vời. Awk được thiết kế để xử lý văn bản và báo cáo, nhưng nó có nhiều tính năng được phát triển tốt cho phép lập trình nghiêm túc. Tuy nhiên, không giống như một số ngôn ngữ khác, cú pháp của awk quen thuộc và mượn những gì tốt nhất từ ​​các ngôn ngữ như C, python và bash (mặc dù chính thức awk đã được tạo trước python và bash). Awk là một trong những ngôn ngữ mà một khi đã học được sẽ trở thành một phần quan trọng trong kho vũ khí chiến lược của lập trình viên.

Bước đầu tiên trong awk

Hãy bắt đầu và thử trải nghiệm với awk để xem nó hoạt động như thế nào nhé. Tại dòng lệnh, nhập lệnh sau: $ awk "( print )" /etc/passwd

Kết quả sẽ hiển thị nội dung của tệp /etc/passwd. Bây giờ - giải thích về những gì awk đã làm. Khi gọi awk, chúng tôi đã chỉ định /etc/passwd làm tệp đầu vào. Khi chúng tôi chạy awk, nó xử lý lệnh in cho từng dòng trong /etc/passwd theo thứ tự. Tất cả đầu ra được gửi đến thiết bị xuất chuẩn và chúng tôi nhận được kết quả tương tự như cat /etc/passwd. Bây giờ hãy giải thích khối ( print ). Trong awk, dấu ngoặc nhọn được sử dụng để nhóm các khối văn bản, giống như trong C. Trong khối văn bản của chúng ta, chỉ có một lệnh in. Trong awk, lệnh in không có bất kỳ tham số bổ sung nào sẽ in toàn bộ nội dung của dòng hiện tại.

Đây là một ví dụ khác về chương trình awk thực hiện điều tương tự: $ awk "( print $0 )" /etc/passwd

Trong awk, biến $0 đại diện cho toàn bộ dòng hiện tại, vì vậy print và print $0 thực hiện chính xác điều tương tự. Nếu muốn, bạn có thể tạo một chương trình trong awk để xuất dữ liệu hoàn toàn không liên quan đến dữ liệu đầu vào. Đây là một ví dụ: $ awk "( print "" )" /etc/passwd

Khi bạn truyền chuỗi "" cho lệnh in, nó luôn in một chuỗi trống. Nếu bạn kiểm tra tập lệnh này, bạn sẽ thấy rằng awk xuất ra một dòng trống cho mỗi dòng trong /etc/passwd. Điều này một lần nữa xảy ra vì awk thực thi một tập lệnh cho mỗi dòng trong tệp đầu vào. Đây là một ví dụ khác: $ awk "( print "hiya" )" /etc/passwd

Nếu bạn chạy tập lệnh này, nó sẽ lấp đầy màn hình với dòng chữ "yay". :)

Nhiều trường

Awk rất phù hợp để xử lý văn bản được chia thành nhiều trường logic và giúp bạn dễ dàng truy cập từng trường riêng lẻ từ bên trong tập lệnh awk. Đoạn script sau sẽ in danh sách tất cả các tài khoản trên hệ thống: $ /etc/passwd

Trong lệnh gọi awk trong ví dụ trên, tùy chọn –F chỉ định ://: dấu phân cách trường. Khi xử lý lệnh print $1, awk sẽ in trường đầu tiên gặp trên mỗi dòng của tệp đầu vào. Đây là một ví dụ khác: $ awk -F: // "( print $1 $3 )" /etc/passwd

Đây là một đoạn từ màn hình đầu ra của tập lệnh này:halt7

Như bạn có thể thấy, awk xuất ra trường thứ nhất và thứ ba của tệp /etc/passwd, tương ứng là trường tên người dùng và uid. Đồng thời, mặc dù tập lệnh hoạt động nhưng nó không hoàn hảo - không có khoảng cách giữa hai trường đầu ra! Những người đã quen với việc lập trình bằng bash hoặc python có thể mong đợi lệnh print $1 $3 sẽ chèn khoảng trắng giữa hai trường này. Tuy nhiên, khi hai dòng nằm cạnh nhau trong chương trình awk, awk sẽ nối chúng mà không thêm khoảng trắng giữa chúng. Lệnh sau sẽ chèn khoảng trắng giữa các trường: $ awk -F"> "( print $1 " " $3 )" /etc/passwd

Khi bản in được gọi theo cách này, nó nối $1, " " và $3 thành chuỗi, tạo ra kết quả mà con người có thể đọc được trên màn hình. Tất nhiên, chúng ta cũng có thể chèn nhãn trường nếu cần: $ awk -F://"( print "username: " $1 "\t\tuid: " $3" )" /etc/passwd

Kết quả là, chúng tôi nhận được kết quả đầu ra sau: tên người dùng: dừng uid:7

tên người dùng: toán tử uid:11

tên người dùng: gốc uid:0

tên người dùng: tắt máy uid:6

tên người dùng: đồng bộ uid:5

tên người dùng:bin uid:1

Tập lệnh bên ngoài

Việc chuyển các tập lệnh tới awk làm đối số dòng lệnh có thể thuận tiện cho những người viết một dòng nhỏ, nhưng khi nói đến các chương trình nhiều dòng phức tạp, chắc chắn tốt hơn là soạn tập lệnh dưới dạng tệp bên ngoài. Sau đó, bạn có thể trỏ awk tới tệp tập lệnh này bằng tùy chọn -f:$ awk -f myscript.awk myfile.in

Việc đặt tập lệnh vào các tệp văn bản riêng biệt cũng cho phép bạn tận dụng các lợi ích bổ sung của awk. Ví dụ: tập lệnh nhiều dòng sau đây thực hiện tương tự như một trong các tập lệnh một dòng trước đó của chúng tôi - nó in trường đầu tiên của mỗi dòng từ /etc/passwd: BEGIN (

Sự khác biệt giữa hai phương pháp này là cách chúng tôi chỉ định dấu phân cách trường. Trong tập lệnh này, dấu phân cách trường được chính chương trình chỉ định nội bộ (bằng cách đặt biến FS), trong khi ở ví dụ trước của chúng tôi, FS được định cấu hình bằng cách chuyển awk tùy chọn -F: // trên dòng lệnh. Thông thường, tốt nhất bạn nên chỉ định dấu phân cách trường trong chính tập lệnh, đơn giản vì nó sẽ không yêu cầu bạn phải nhớ một đối số dòng lệnh khác. Chúng ta sẽ xem xét biến FS chi tiết hơn ở phần sau của bài viết này.

Khối BEGIN và END

Thông thường, awk thực thi mỗi khối trong văn bản tập lệnh một lần cho mỗi dòng đầu vào. Tuy nhiên, thường có những tình huống trong lập trình mà bạn cần thực thi mã khởi tạo trước khi awk bắt đầu xử lý văn bản từ tệp đầu vào. Đối với những trường hợp như vậy, awk cung cấp khả năng xác định khối BEGIN. Chúng ta đã sử dụng khối BEGIN trong ví dụ trước. Vì khối BEGIN được xử lý trước khi awk bắt đầu xử lý tệp đầu vào, nên đây là nơi tuyệt vời để khởi tạo biến FS (dấu tách trường), xuất tiêu đề hoặc khởi tạo các biến toàn cục khác sẽ được sử dụng sau này trong chương trình.

Awk cũng cung cấp một khối đặc biệt khác gọi là khối END. Awk thực thi khối này sau khi tất cả các dòng trong tệp đầu vào đã được xử lý. Thông thường, khối END được sử dụng để thực hiện các phép tính cuối cùng hoặc kết quả đầu ra sẽ xuất hiện ở cuối luồng đầu ra.

Biểu thức và khối thông thường

Awk cho phép bạn sử dụng các biểu thức chính quy để thực thi có chọn lọc các khối cụ thể của chương trình tùy thuộc vào việc biểu thức chính quy có khớp với dòng hiện tại hay không. Đây là một tập lệnh mẫu chỉ in những dòng chứa chuỗi ký tự foo:/foo/ ( print )

Tất nhiên, bạn có thể sử dụng các biểu thức chính quy phức tạp hơn. Đây là tập lệnh sẽ chỉ in các dòng có chứa dấu phẩy: /+\.*/ ( print )

Biểu thức và khối

Có nhiều cách khác để thực thi có chọn lọc một khối chương trình. Chúng ta có thể đặt bất kỳ biểu thức Boolean nào trước khối chương trình để điều khiển việc thực thi khối đó. Awk sẽ chỉ thực thi một khối chương trình nếu biểu thức Boolean trước đó có giá trị đúng. Tập lệnh ví dụ sau sẽ xuất ra trường thứ ba của tất cả các dòng có trường đầu tiên được đánh dấu. Nếu trường đầu tiên của dòng hiện tại không có fred, awk sẽ tiếp tục xử lý tệp và sẽ không đưa ra câu lệnh in cho dòng hiện tại: :$1 == "fred" ( print $3 )

Awk cung cấp một bộ đầy đủ các toán tử so sánh, bao gồm cả các toán tử "==", " thông thường<", ">", "<=", ">=" và "!=". Ngoài ra, awk còn cung cấp các toán tử "~" và "!~", có nghĩa là "khớp" và "không khớp". Chúng đặt biến ở bên trái toán tử và biểu thức chính quy ở bên phải của nó. Đây là một ví dụ trong đó chỉ trường thứ ba của một dòng được in nếu trường thứ năm của cùng dòng chứa chuỗi ký tự root:$5 ~ /root/ ( print $3 )

Câu điều kiện

Awk cũng cung cấp các câu lệnh if giống C rất hay. Nếu muốn, bạn có thể viết lại tập lệnh trước đó bằng cách sử dụng if:(

nếu ($5 ~ /root/) (

Cả hai tập lệnh đều hoạt động giống hệt nhau. Trong ví dụ đầu tiên, biểu thức boolean nằm ngoài khối, trong khi ở ví dụ thứ hai, khối được thực thi cho mỗi dòng đầu vào và chúng tôi thực thi có chọn lọc lệnh in bằng câu lệnh if. Cả hai phương thức đều hoạt động và bạn có thể chọn một phương thức. kết hợp tốt nhất với các phần khác của kịch bản.

Đây là một ví dụ phức tạp hơn về câu lệnh if trong awk. Như bạn có thể thấy, ngay cả với các điều kiện lồng nhau phức tạp, các câu lệnh if trông giống hệt với các câu lệnh C của chúng :(

nếu ($1 == "foo") (

nếu ($2 == "foo") (

) khác nếu ($1 == "bar") (

Sử dụng câu lệnh if, chúng ta có thể chuyển đổi mã này: ! /matchme/ ( in $1 $3 $4 )

như thế này: (

if ($0 !~ /matchme/) (

Cả hai tập lệnh sẽ chỉ in các dòng không chứa chuỗi ký tự matchme. Và trong trường hợp này cũng vậy, bạn có thể chọn một phương pháp hoạt động tốt hơn trong một chương trình cụ thể. Cả hai đều làm điều tương tự.

Awk cũng cung cấp cho bạn khả năng sử dụng toán tử Boolean "||" ("logic OR") và "&&" ("logic AND"), cho phép bạn tạo các biểu thức Boolean phức tạp hơn: ($1 == "foo") && ($2 == "bar") ( print )

Ví dụ này sẽ chỉ xuất ra các hàng có trường đầu tiên là foo và trường thứ hai là bar.

Biến số!

Cho đến nay chúng tôi đã in các biến chuỗi, toàn bộ chuỗi hoặc các trường cụ thể. Tuy nhiên, awk cũng cho chúng ta khả năng thực hiện so sánh trên cả số nguyên và số dấu phẩy động. Sử dụng các biểu thức toán học, rất dễ dàng để viết một tập lệnh đếm số dòng trống trong một tệp. Đây là một tập lệnh như vậy: BEGIN ( x=0 )

KẾT THÚC ( in "Đã tìm thấy " x " dòng trống. :)"

Trong khối BEGIN, chúng ta khởi tạo biến số nguyên x về 0. Sau đó, mỗi khi awk gặp một dòng trống, nó sẽ thực thi câu lệnh x=x+1, tăng x thêm 1. Khi tất cả các dòng đã được xử lý, khối END sẽ được thực thi và awk sẽ in tổng số cuối cùng, cho biết số dòng trống được tìm thấy.

Biến chuỗi

Một trong những điều thú vị về các biến awk là chúng là "chữ thường và chữ thường". Tôi gọi các biến awk là "chuỗi" vì tất cả các biến awk đều được lưu trữ nội bộ dưới dạng chuỗi. Đồng thời, các biến awk rất "đơn giản" vì bạn có thể thực hiện phép toán trên một biến và nếu nó chứa một chuỗi số hợp lệ, awk sẽ tự động đảm nhiệm việc chuyển đổi chuỗi đó thành một số. Để hiểu ý tôi, hãy xem ví dụ này: x="1.01"

# Chúng ta đã tạo x chứa *string* "1.01"

# Chúng tôi vừa thêm 1 vào *string*

# Nhân tiện, đây là một bình luận :)

Awk sẽ xuất ra: 2,01

Tò mò! Mặc dù chúng tôi đã gán giá trị chuỗi 1,01 cho x nhưng chúng tôi vẫn có thể thêm một giá trị vào chuỗi đó. Chúng tôi không thể làm điều này trong bash hoặc python. Trước hết, bash không hỗ trợ số học dấu phẩy động. Và mặc dù bash có các biến "chuỗi", nhưng chúng không "đơn giản"; Để thực hiện bất kỳ phép toán nào, bash yêu cầu chúng ta gói các phép tính của mình trong các cấu trúc $() xấu xí. Nếu chúng ta đang sử dụng python, chúng ta sẽ cần chuyển đổi rõ ràng chuỗi 1.01 thành giá trị dấu phẩy động trước khi thực hiện bất kỳ phép tính nào với nó. Mặc dù điều này không khó nhưng vẫn là một bước bổ sung. Trong trường hợp awk, tất cả điều này được thực hiện tự động và nó làm cho mã của chúng tôi đẹp và rõ ràng. Nếu cần bình phương trường đầu tiên của mỗi chuỗi đầu vào và thêm một trường vào đó, chúng ta sẽ sử dụng tập lệnh như thế này:( print ($1^2)+1 )

Nếu thử nghiệm một chút, bạn sẽ thấy rằng nếu một biến không chứa số hợp lệ, awk sẽ coi biến đó là số 0 khi đánh giá một biểu thức toán học.

Nhiều nhà khai thác

Một tính năng thú vị khác của awk là bộ toán tử hoàn chỉnh. Ngoài phép cộng, trừ, nhân và chia tiêu chuẩn, awk còn cho chúng ta khả năng sử dụng toán tử số mũ đã được trình bày trước đó "^", toán tử số dư chia số nguyên "%" và nhiều toán tử gán thuận tiện khác mượn từ C.

Chúng bao gồm các toán tử gán trước và sau tăng/giảm (i++, --foo), các toán tử gán với phép cộng/trừ/nhân/chia (a+=3, b*=2, c/=2.2, d-=6.2) . Nhưng đó không phải là tất cả - chúng tôi còn có các toán tử gán thuận tiện để tính phần còn lại của phép chia số nguyên và lũy thừa (a^=2, b%=4).

Dấu phân cách trường

awk có bộ biến đặc biệt riêng. Một số trong số chúng cung cấp cho bạn khả năng tinh chỉnh cách hoạt động của awk, trong khi một số khác chứa thông tin đầu vào có giá trị. Chúng ta đã đề cập đến một trong những biến đặc biệt này, FS. Như đã đề cập trước đó, biến này cho phép bạn chỉ định chuỗi ký tự mà awk sẽ coi là dấu phân cách trường. Khi chúng tôi sử dụng /etc/passwd làm đầu vào, FS được đặt thành ://. Điều này hóa ra là đủ, nhưng FS còn mang lại cho chúng tôi sự linh hoạt hơn nữa.

Giá trị của biến FS không nhất thiết phải là một ký tự đơn; nó có thể được gán một biểu thức chính quy chỉ định mẫu ký tự có độ dài bất kỳ. Nếu bạn đang xử lý các trường được phân tách bằng một hoặc nhiều ký tự tab thì FS phải được định cấu hình như sau: FS="\t+"

Ở trên chúng tôi đã sử dụng ký tự biểu thức chính quy đặc biệt "+", có nghĩa là "một hoặc nhiều lần xuất hiện của ký tự trước đó".

Nếu các trường được phân tách bằng khoảng trắng (một hoặc nhiều dấu cách hoặc tab), bạn có thể muốn đặt FS thành biểu thức chính quy sau:FS="[[:space:]+]"

Mặc dù thiết lập này sẽ hoạt động nhưng nó không cần thiết. Tại sao? Bởi vì giá trị mặc định của FS là một ký tự khoảng trắng, được awk hiểu là "một hoặc nhiều khoảng trắng hoặc tab". Trong ví dụ cụ thể của chúng tôi, giá trị FS mặc định chính xác là những gì chúng tôi cần!

Cũng không có vấn đề gì với các biểu thức chính quy phức tạp. Ngay cả khi các bản ghi được phân tách bằng từ "foo" theo sau là ba chữ số, biểu thức chính quy sau đây sẽ phân tích dữ liệu một cách chính xác: FS="foo"

Số lượng trường

Hai biến tiếp theo mà chúng ta sẽ xem xét thường không được dùng để ghi vào mà được sử dụng để đọc và lấy thông tin hữu ích về đầu vào. Đầu tiên trong số này là biến NF, còn được gọi là số lượng trường. Awk tự động đặt giá trị của biến này theo số trường trong bản ghi hiện tại. Bạn có thể sử dụng biến NF để chỉ hiển thị một số dòng đầu vào nhất định: NF == 3 ( print "có ba trường trong mục này: " $0 )

Tất nhiên, biến NF cũng có thể được sử dụng trong các câu lệnh điều kiện, ví dụ:(

nếu (NF > 2) (

in $1 " " $2 => $3

Con số kỷ lục

Một biến thuận tiện khác là số bản ghi (NR). Nó luôn chứa số của bản ghi hiện tại (awk coi bản ghi đầu tiên là bản ghi số 1). Cho đến nay chúng ta đã xử lý các tệp đầu vào chứa một bản ghi trên mỗi dòng. Trong những tình huống như vậy, NR cũng sẽ báo cáo số dòng hiện tại. Tuy nhiên, khi chúng ta bắt đầu xử lý các bản ghi nhiều dòng trong các bài viết sau của loạt bài này, điều này sẽ không còn xảy ra nữa, vì vậy bạn cần phải cẩn thận! NR có thể được sử dụng giống như biến NF để chỉ xuất ra một số dòng đầu vào nhất định:(NR< 10) || (NR >100) ( in "Chúng tôi đang ở số kỷ lục 1-9 hoặc 101 trở lên") )

Thêm một ví dụ nữa :(

nếu (NR > 10) (

in "bây giờ thông tin thực sự đang đến!"

Awk cung cấp các biến bổ sung có thể được sử dụng cho nhiều mục đích khác nhau. Chúng ta sẽ xem xét các biến này trong các bài viết sau. Chúng ta đã kết thúc quá trình khám phá ban đầu về awk. Trong các bài viết tiếp theo của loạt bài này, tôi sẽ trình bày chức năng awk nâng cao hơn và chúng tôi sẽ kết thúc loạt bài này bằng một ứng dụng awk trong thế giới thực. Trong thời gian chờ đợi, nếu muốn tìm hiểu thêm, bạn có thể xem các tài nguyên được liệt kê bên dưới.