Lệnh awk trong Linux là một công cụ mạnh mẽ và linh hoạt dành cho việc xử lý và phân tích dữ liệu dạng văn bản. Được đặt tên theo ba nhà phát triển ban đầu của nó: Alfred Aho, Peter Weinberger và Brian Kernighan, awk giúp người dùng dễ dàng duyệt, tìm kiếm và thao tác các dòng và cột trong tệp văn bản. Trong bài hướng dẫn này, hãy cùng Vietnix sẽ tìm hiểu cách sử dụng lệnh awk trong Linux để thực hiện các nhiệm vụ cho hệ thống.
Lệnh awk trong Linux hoạt động như thế nào?
HIện nay có một số cách triển khai khác nhau của awk. VIetnix sẽ sử dụng triển khai GNU của awk, được gọi là gawk. Trên hầu hết các hệ thống Linux, trình thông dịch awk chỉ là một liên kết tượng trưng (symlink) đến gawk.
Bạn đọc tham khảo thêm một số lệnh khác trong Linux mà Vietnix đã tổng hợp:
Record và Field
Awk có thể xử ký các file và luồng dữ liệu dạng văn bản. Dữ liệu đầu vào được chia thành các bản ghi (record) và trường (field). Awk hoạt động trên từng bản ghi cho đến khi kết thúc đầu vào. Các bản ghi được phân tách bằng ký tự phân tách bản ghi (record separator – RS), trong đó ký tự mặc định là newline. Khi đó mỗi dòng trong dữ liệu văn bản sẽ được xem là một bản ghi. Các ký tự tách bản ghi có thể được cài đặt bằng biến RS.
Bản ghi chứa các trường và được phân chia bởi ký tự tách trường (field separator). Theo mặc định thì ký tự này là khoảng trắng, trong đó gồm một hay nhiều ký tự tab, space hoặc ký tự newline. Các trường trong một bản ghi được tham chiếu bằng ký hiệu $, theo sau là mã số trường và bắt đầu bằng số 1. Vì vậy, trường đầu tiên được biểu diễn là $1, trường thứ hai là $2,… Trường cuối cùng có tham chiếu đặc biệt là $NF, và toàn bộ bản ghi được tham chiếu bằng mã $0.
Ví dụ về cách tham chiếu bản ghi và trường:
tmpfs 788M 1.8M 786M 1% /run/lock /dev/sda1 234G 191G 31G 87% / |-------| |--| |--| |--| |-| |--------| $1 $2 $3 $4 $5 $6 ($NF) --> fields |-----------------------------------------| $0 --> record
Chương trình awk
Để xử lý văn bản bằng awk, bạn có thể viết một chương trình chứa các quy tắc và hàm tự định nghĩa để chỉ dẫn cho lệnh thực hiện. Mỗi quy tắc chứa một mẫu và cặp hành động (action). Các quy tắc được phân tách nhau bằng ký tự newline hoặc dấu “;”. Thường thì một chương trình awk sẽ có dạng như sau:
pattern { action } pattern { action } ...
Khi awk xử lý dữ liệu, nếu mẫu khớp với bản ghi thì awk sẽ thực hiện action được chỉ định trong bản ghi này. Nếu quy tắc không có mẫu thì toàn bộ bản ghi sẽ được khớp.
Một hành awk action được định nghĩa bởi các câu lệnh đặt trong cặp dấu ngoặc ngọn “{…}”. Một action có thể có nhiều hơn một câu lệnh, các câu lệnh được phân tách nhau bởi ký tự newline hoặc dấu ;. Nếu quy tắc không có action nào thì awk sẽ in toàn bộ bản ghi theo mặc định.
Awk hỗ trợ rất nhiều loại câu lệnh khác nhau, bao gồm cả biểu thức, điều kiện, câu lệnh input/output,… Các lệnh awk phổ biến nhất gồm có:
- exit – Dừng quá trình thực thi toàn bộ chương trình và thoát.
- next – Dừng thực thi bản ghi hiện tại và chuyển sang bản ghi tiếp theo trong dữ liệu đầu vào.
- print – In ra các bản ghi, trường, biến và văn bản.
- printf – Cho phép người dùng kiểm soát định dạng output tốt hơn, tương tự như C và bash printf.
Khi viết các chương trình awk, mọi thứ đằng sau dấu # cho đến cuối dòng đều được xem là một comment. Các dòng văn bản dài có thể được ngắt xuống dòng bằng ký tự \.
Thực thi chương trình awk
Một chương trình awk có thể chạy theo nhiều phương pháp khác nhau. Nếu chương trình tương đối ngắn và đơn giản, bạn có thể truyền trực tiếp vào trình phiên dịch của awk trong command-line:
awk 'program' input-file...
Khi chạy chương trình trên command-line, bạn nên đặt trong cặp dấu nháy đơn (‘…’) để shell không phiên dịch chương trình.
Nếu cần chạy một chương trình lớn và phức tạp thì bạn nền đặt chương trình này vào một file rồi dùng option -f
để truyền file vào lệnh awk trong Linux:
awk -f program-file input-file...
Trong bài viết này, các ví dụ minh họa sẽ xoay quanh file teams.txt có nội dung như sau:
Bucks Milwaukee 60 22 0.732 Raptors Toronto 58 24 0.707 76ers Philadelphia 51 31 0.622 Celtics Boston 49 33 0.598 Pacers Indiana 48 34 0.585
Mẫu awk
Mẫu trong awk có nhiệm vụ kiểm soát xem action có nên được thực hiện hay không. Awk hỗ trợ nhiều kiểu khớp mẫu khác nhau như: biểu thức chính quy, biểu thức quan hệ, mẫu biểu thức đặc biệt,…
Nếu quy tắc không định nghĩa mẫu nào thì từng bản ghi sẽ được khớp. Giả sử bạn có một quy tắc chỉ chứa duy nhất một action:
awk '{ print $3 }' teams.txt
Bây giờ chương trình sẽ in ra trường thứ ba cảu mỗi bản ghi:
60 58 51 49 48
Mẫu biểu thức chính quy
Biểu thức chính quy (regular expression/regex) là một mẫu có khả năng khớp một tập hợp các chuỗi. Một mẫu regex của awk được đặt trong cặp dấu //:
/regex pattern/ { action }
Ví dụ bạn muốn hiển thị trường đầu tiên của từng bản ghi có chứa chuỗi 0.5:
awk '/0.5/ { print $1 }' teams.txt
Output:
Celtics Pacers
Mẫu có thể là bất kỳ kiểu regex nào. Bạn cũng có thể in ra trường đầu tiên nếu bản ghi bắt đầu bằng hai chữ số trở lên:
awk '/^[0-9][0-9]/ { print $1 }' teams.txt
Output:
76ers
Mẫu biểu thức quan hệ
Các mẫu biểu thức quan hệ thường được dùng để khớp nội dung của một trường hay biến nhất định. Theo mặc định, các mẫu regex được khớp dựa trên bản ghi. Để khớp regex theo trường thì bạn cần chỉ định trường và dùng toán tử so sánh contain (~).
Giả sử bạn cần in trường đầu tiên của môi bản ghi, trong đó trường thứ hai chứa chuỗi “ia“:
awk '$2 ~ /ia/ { print $1 }' teams.txt
Output:
76ers Pacers
Ngược lại, nếu muốn khớp các trường không chứa mẫu thì bạn có thể dùng toán tử !~:
awk '$2 !~ /ia/ { print $1 }' teams.txt
Output:
Bucks Raptors Celtics
Ngoài ra bạn cũng có thể so sánh các chuỗi và chữ số với nhau, với các toán tử như: lớn hơn, bằng, nhỏ hơn,…
Ví dụ sau cần in ra trường đầu tiên của mọi bản ghi có trường giá trị trường thứ ba lớn hơn 50:
awk '$3 > 50 { print $1 }' teams.txt
Output:
Bucks Raptors 76ers
Mẫu theo khoảng
Mẫu theo khoảng (range pattern) gồm hai mẫu phân tách nhau bởi dấu phẩy:
pattern1, pattern2
Mẫu này sẽ khớp mọi bản ghi bắt đầu từ pattern1 đến pattern2. Lấy ví dụ, câu lệnh dưới đây sẽ in ra trường đầu tiên của mọi bản ghi, bắt đầu từ bản ghi chứa chuỗi “Raptors” cho đến bản ghi chứa chuỗi “Celtics”:
awk '/Raptors/,/Celtics/ { print $1 }' teams.txt
Output:
Raptors 76ers Celtics
Ngoài ra, mẫu cũng có thể là các biểu thức quan hệ. Ví dụ lệnh dưới đây có nhiệm vụ in ra mọi bản ghi, bắt đầu từ bản ghi có giá trị của trường thứ tư là 32, đến bản ghi có trường thứ tư bằng 33:
awk '$4 == 31, $4 == 33 { print $0 }' teams.txt
Output:
76ers Philadelphia 51 31 0.622 Celtics Boston 49 33 0.598
Các mẫu biểu thức đặc biệt
Lệnh awk
trong Linux cũng cung cấp một số mẫu đặc biệt như sau:
- BEGIN – Dùng để thực hiện các action trước khi xử lý bản ghi.
- END – Dùng để thực hiện action sau khi xử lý bản ghi.
Mẫu BEGIN thường dùng để đặt biến và mẫu END có thể dùng để xử lý dữ liệu từ các bản ghi.
Ví dụ đoạn code sau cần in ra chuỗi “Start Processing”, sau đó in trường thứ ba của mỗi bản ghi rồi kết thúc bằng chuỗi “End Processing”:
awk 'BEGIN { print "Start Processing." }; { print $3 }; END { print "End Processing." }' teams.txt
Output:
Start Processing
60
58
51
49
48
End Processing.
Nếu một chương trình chỉ có mẫu BEGIN thì câu lệnh chỉ thực hiện các action mà không xử lý inout. Ngược lại, nếu chương trình chỉ có mẫu END thì lệnh sẽ xử lý input trước khi thực hiện các action.
Kết hợp các mẫu trong awk
Awk cho phép kết hợp hai hay nhiều mẫu khác nhau bằng toán tử logic AND (&&) và OR (||).
Ví dụ dùng toán tử && để in ra trường đầu tiên của các bản ghi có giá trị của trường thứ ba lớn hơn 50, đồng thời giá trị của trường thứ tư cũng lớn hơn 30:
awk '$3 > 50 && $4 < 30 { print $1 }' teams.txt
Output:
Bucks Raptors
Các biến có sẵn trong awk
Awk cung cấp một số biến có sẵn chứa các thông tin hữu ích, đồng thời cho phép kiểm soát cách xử lý dữ liệu của chương trình. Một số biến phổ biến trong awk gồm có:
- NF – Số lượng trường có trong bản ghi.
- NR – Số lượng bản ghi hiện tại.
- FILENAME – Tên của file input đang được xử lý.
- FS – Ký tự phân tách trường (field separator).
- RS – Ký tự phân tách bản ghi (record separator).
- OFS – Ký tự phân tách trường đầu ra (output field separator).
- ORS – Ký tự phân tách bản ghi đầu ra (output record separator).
Ví dụ bạn cần in tên file và số lượng dòng (bản ghi) trong đó:
awk 'END { print "File", FILENAME, "contains", NR, "lines." }' teams.txt
Output:
File teams.txt contains 5 lines.
Các biến trong awk có thể được đặt ở bất kỳ dòng nào trong chương trình. Để định nghĩa một biến cho toàn bộ chương trình, bạn có thể đặt trong mẫu BEGIN.
Thay đổi ký tự phân tách trường và bản ghi
Ký tự phân tách mặc định của trường là tab hoặc space. Bạn có thể thay đổi thông qua biến FS. Giả sử muốn đặt thành dấu .:
File teams.txt contains 5 lines.
Output:
Bucks Milwaukee 60 22 0 Raptors Toronto 58 24 0 76ers Philadelphia 51 31 0 Celtics Boston 49 33 0 Pacers Indiana 48 34 0
Ký tự phân tách trường cũng có thể được đặt thành nhiều ký tự:
awk 'BEGIN { FS = ".." } { print $1 }' teams.txt
Khi chạy các lệnh awk chỉ có một dòng trong command-line, bạn có thể dùng option -F để đổi ký tự phân tách trường:
awk -F "." '{ print $1 }' teams.txt
Theo mặc định thì ký tự phân tách bản ghi là newline, và cũng có thể được đổi qua biến RS.
Tương tự, giả sử bạn muốn đổi thành ký tự .:
awk -F "." '{ print $1 }' teams.txt
Output:
Bucks Milwaukee 60 22 0 732 Raptors Toronto 58 24 0 707 76ers Philadelphia 51 31 0 622 Celtics Boston 49 33 0 598 Pacers Indiana 48 34 0 585
Các action trong awk
Các action có thể được đặt trong cặp dấu {} và thực thi khi mẫu được khớp. Một action có thể có 0 hay nhiều câu lệnh. Các câu lệnh được thực hiện lần lượt và phân tách nhau bởi ký tự newline hoặc dấu ;.
Một số loại câu lệnh action trong awk:
- Biểu thức, chẳng hạn như gán biến, toán tử số học,…
- Câu lệnh điều khiển (
if
,for
,while
,switch
,…) - Lệnh output (
print
,printf
) - Câu lệnh ghép
- Câu lệnh input
- Câu lệnh xóa
Lệnh print
chắc chắn là một trong những lệnh awk
phổ biến nhất, dùng để hiển thị output của văn bản, bản ghi, trường và biến.
Khi in nhiều giá trị khác nhau, bạn cần phân tách nhau bằng dấu phẩy. Ví dụ:
awk '{ print $1, $3, $5 }' teams.txt
Output (mặc định thì các item được tách nhau bởi một khoảng trắng):
Bucks 60 0.732 Raptors 58 0.707 76ers 51 0.622 Celtics 49 0.598 Pacers 48 0.585
Nếu không dùng dấu phẩy thì sẽ không có khoảng trắng giữa các item:
awk '{ print $1 $3 $5 }' teams.txt
Output:
Bucks600.732 Raptors580.707 76ers510.622 Celtics490.598 Pacers480.585
Nếu dùng lệnh print mà không có tham số thì option mặc định sẽ là print $0, tức là in ra bản ghi hiện tại.
Để in một đoạn văn bản, bạn có thể đặt trong cặp dấu ngoặc kép:
awk '{ print "The first field:", $1}' teams.txt
Output:
The first field: Bucks The first field: Raptors The first field: 76ers The first field: Celtics The first field: Pacers
Bạn cũng có thể in các ký tự đặc biệt như newline:
awk 'BEGIN { print "First line\nSecond line\nThird line" }'
Output:
First line Second line Third line
Lệnh printf cho phép kiểm soát định dạng output tốt hơn. Giả sử bạn cần đánh chỉ số dòng khi in ra màn hình:
awk '{ printf "%3d. %s\n", NR, $0 }' teams.txt
Output:
1. Bucks Milwaukee 60 22 0.732 2. Raptors Toronto 58 24 0.707 3. 76ers Philadelphia 51 31 0.622 4. Celtics Boston 49 33 0.598 5. Pacers Indiana 48 34 0.585
Khi viết các chương trình dài, bạn có thể tạo một file chương trình awk riêng như sau:
BEGIN { i = 1 while (i < 6) { print "Square of", i, "is", i*i; ++i } }
Chạy chương trình bằng cách truyền tên file vào trình phiên dịch của awk:
awk -f prg.awk
Sử dụng biến Shell trong chương trình awk
Nếu đang dùng lệnh awk trong shell script thì có thể bạn sẽ cần chuyển một biến shell cho chương trình awk. Khi đó, bạn có thể đặt chương trình trong cặp dấu nháy kép (thay vì nháy đơn) rồi thay tên biến trong chương trình. Tuy nhiên, việc này lại vô tình khiến chương trình awk trở nên phức tạp hơn.
Mặt khác, bạn có thể dùng các biến shell trong chương trình awk bằng cách gán các biến này vào một biến awk. Ví dụ:
awk -v n="$num" 'BEGIN {print n}'
Output:
51
Lời kết
Awk là một trong những công cụ xử lý văn bản mạnh mẽ nhất trong Linux. Bài viết này đã cung cấp những kiến thức và cách sử dụng lệnh awk trong Linux cơ bản nhất. Bạn đọc có thể khảo thêm tài liệu hướng dẫn của awk để hiểu rõ hơn về công cụ này. Chúc các bạn thành công!