Giới thiệu về công cụ grep
grep (viết tắt của global regular expression print) là một tiện ích dòng lệnh mạnh mẽ trong các hệ thống Unix và Linux, được thiết kế để tìm kiếm các mẫu văn bản cụ thể trong các tệp. Nó sử dụng các biểu thức chính quy (regular expressions – regex) để xác định các dòng phù hợp với mẫu tìm kiếm và hiển thị chúng.
Trong họ grep trên Unix, chúng ta có grep, egrep và fgrep. Các phiên bản này có một số khác biệt nhỏ:
egrep(hoặcgrep -E) là phiên bản mở rộng, hỗ trợ nhiều ký tự regex đặc biệt hơn mà không cần thoát.fgrep(hoặcgrep -F) là phiên bản "fast grep" hoặc "fixed grep", chuyên dùng để tìm kiếm các chuỗi cố định (literal strings). Nó coi tất cả các ký tự là văn bản thông thường, không diễn giải chúng như các ký tự regex đặc biệt.
Các hệ thống Linux hiện đại thường sử dụng phiên bản GNU grep, cung cấp các tính năng mạnh mẽ và có thể mô phỏng chức năng của egrep và fgrep thông qua các tùy chọn dòng lệnh như -E và -F.
Các tùy chọn grep thường dùng
Cú pháp cơ bản của lệnh grep:
grep [TÙY_CHỌN] 'MẪU_TÌM_KIẾM' TÊN_TỆP
Một số tùy chọn phổ biến:
-a: Xử lý tệp nhị phân (binary files) như tệp văn bản khi tìm kiếm.-c: Đếm số lượng dòng khớp với mẫu tìm kiếm thay vì in ra các dòng đó.-i: Bỏ qua phân biệt chữ hoa/thường khi tìm kiếm.-n: Hiển thị số dòng cùng với các dòng khớp.-v: Đảo ngược tìm kiếm, hiển thị các dòng KHÔNG khớp với mẫu.--color=auto: Tự động tô màu cho các phần văn bản khớp với mẫu tìm kiếm, giúp dễ nhìn hơn.
Ví dụ cơ bản
Để tìm tất cả các dòng chứa từ "root" trong tệp /etc/passwd:
grep root /etc/passwd
# Hoặc sử dụng pipe từ cat
cat /etc/passwd | grep root
Kết quả sẽ là các dòng như:
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
Hiển thị các dòng chứa "root" cùng với số thứ tự dòng:
grep -n root /etc/passwd
Kết quả:
1:root:x:0:0:root:/root:/bin/bash
30:operator:x:11:0:operator:/root:/sbin/nologin
Để dễ dàng nhận biết kết quả, bạn có thể bật tính năng tô màu. Nếu không muốn gõ --color=auto mỗi lần, bạn có thể thêm một alias vào tệp ~/.bashrc:
alias grep='grep --color=auto'
Sau đó chạy source ~/.bashrc để áp dụng thay đổi ngay lập tức.
Tìm các dòng KHÔNG chứa "root" trong /etc/passwd:
grep -v root /etc/passwd
Để loại trừ cả "root" và "nologin", bạn có thể kết hợp nhiều lệnh grep -v:
grep -v root /etc/passwd | grep -v nologin
Sử dụng dmesg để hiển thị thông tin kernel, sau đó tìm các dòng chứa "eth", hiển thị số dòng và tô màu từ khóa:
dmesg | grep -n --color=auto 'eth'
Kết quả tương tự:
247:eth0: RealTek RTL8139 at 0xee846000, 00:90:cc:a6:34:84, IRQ 10
248:eth0: Identified 8139 chip type 'RTL-8139C'
294:eth0: link up, 100Mbps, full-duplex, lpa 0xC5E1
305:eth0: no IPv6 routers present
Tìm kiếm theo ngữ cảnh (Contextual Search)
Đôi khi bạn cần xem các dòng xung quanh dòng khớp để hiểu rõ hơn ngữ cảnh. Các tùy chọn -A (After) và -B (Before) giúp bạn làm điều này:
-A N: Hiển thịNdòng SAU mỗi dòng khớp.-B N: Hiển thịNdòng TRƯỚC mỗi dòng khớp.-C Nhoặc-N: Hiển thịNdòng TRƯỚC VÀ SAU mỗi dòng khớp.
Ví dụ, tìm dòng chứa "eth" và hiển thị 2 dòng trước đó (-B2) cùng 3 dòng sau đó (-A3):
dmesg | grep -n -A3 -B2 --color=auto 'eth'
Kết quả minh họa:
245-PCI: setting IRQ 10 as level-triggered
246-ACPI: PCI Interrupt 0000:00:0e.0[A] -> Link [LNKB] ...
247:eth0: RealTek RTL8139 at 0xee846000, 00:90:cc:a6:34:84, IRQ 10
248:eth0: Identified 8139 chip type 'RTL-8139C'
249-input: PC Speaker as /class/input/input2
250-ACPI: PCI Interrupt 0000:00:01.4[B] -> Link [LNKB] ...
251-hdb: ATAPI 48X DVD-ROM DVD-R-RAM CD-R/RW drive, 2048kB Cache, UDMA(66)
Các dòng khớp (247 và 248) và các dòng xung quanh chúng đều được hiển thị.
Tìm kiếm đệ quy trong thư mục
Để tìm kiếm trong toàn bộ thư mục con, bạn có thể sử dụng tùy chọn -r (recursive):
grep -r 'energywise' . # Tìm kiếm trong thư mục hiện tại và tất cả thư mục con
Nếu bạn chỉ muốn hiển thị tên các tệp chứa chuỗi tìm kiếm mà không in ra nội dung dòng khớp, hãy dùng -l (files with matches):
grep -l -r 'energywise' . # Chỉ hiển thị tên tệp chứa 'energywise'
grep và Biểu thức Chính quy (Regular Expressions)
Sức mạnh thực sự của grep đến từ khả năng sử dụng biểu thức chính quy để định nghĩa mẫu tìm kiếm phức tạp.
Các ví dụ sau đây giả định bạn có một tệp văn bản tên regular_express.txt để thử nghiệm.
Lớp ký tự (Character Classes)
Dùng để khớp với một trong số các ký tự được liệt kê. Ví dụ, để tìm các từ "test" hoặc "taste", chúng ta có thể sử dụng [ae]:
grep -n 't[ae]st' regular_express.txt
Ký tự bên trong dấu ngoặc vuông [] đại diện cho MỘT ký tự duy nhất. Ví dụ, t[ae]st sẽ khớp với "tast" hoặc "test".
Đảo ngược lớp ký tự [^]: Để tìm các dòng chứa "oo" nhưng KHÔNG có "g" đứng trước nó:
grep -n '[^g]oo' regular_express.txt
Lưu ý: Nếu một dòng chứa "goo" nhưng cũng có "too", dòng đó vẫn sẽ được hiển thị vì "too" khớp với mẫu.
Dãy ký tự liên tiếp: Để tìm các dòng chứa "oo" nhưng KHÔNG có ký tự chữ cái viết thường đứng trước (ví dụ, không phải "foo", "boo", ...), bạn có thể dùng [^a-z]:
grep -n '[^a-z]oo' regular_express.txt
Tương tự, bạn có thể dùng [A-Z] cho chữ cái hoa, [0-9] cho số. Để khớp với chữ cái HOẶC số, kết hợp chúng: [a-zA-Z0-9].
Tìm các dòng chứa ít nhất một chữ số:
grep -n '[0-9]' regular_express.txt
Neo dòng (Anchors) ^ và $
Các ký tự này dùng để neo mẫu tìm kiếm vào đầu hoặc cuối dòng.
^: Khớp với đầu dòng.$: Khớp với cuối dòng.
Để tìm các dòng BẮT ĐẦU bằng "the":
grep -n '^the' regular_express.txt
Để tìm các dòng BẮT ĐẦU bằng một chữ cái viết thường:
grep -n '^[a-z]' regular_express.txt
Để tìm các dòng KHÔNG BẮT ĐẦU bằng một chữ cái:
grep -n '^[^a-zA-Z]' regular_express.txt
Lưu ý: Ký tự ^ có ý nghĩa khác nhau tùy thuộc vào vị trí của nó. Bên trong [], nó là "phủ định" (ví dụ: [^a-z]). Bên ngoài [], nó là "đầu dòng" (ví dụ: ^abc).
Để tìm các dòng KẾT THÚC bằng dấu chấm (.). Dấu chấm là một ký tự đặc biệt trong regex, vì vậy bạn cần thoát nó bằng \:
grep -n '\.$' regular_express.txt
Để tìm các dòng trống (empty lines):
grep -n '^$' regular_express.txt
Dòng trống là dòng bắt đầu và kết thúc ngay lập tức.
Ký tự đại diện . và lặp lại *
.(dấu chấm): Đại diện cho BẤT KỲ MỘT ký tự nào (trừ ký tự xuống dòng).*(dấu sao): Đại diện cho việc lặp lại Ký tự hoặc Biểu thức chính quy ĐỨNG TRƯỚC nó, từ 0 đến vô số lần.
Ví dụ, tìm chuỗi "g" theo sau bởi hai ký tự bất kỳ, và kết thúc bằng "d" (tức là "g??d"):
grep -n 'g..d' regular_express.txt
Điều này sẽ khớp với "good", "glad", v.v., nhưng không khớp với "god" (chỉ có một ký tự giữa g và d).
Nếu bạn muốn tìm các chuỗi có "o" lặp lại ít nhất hai lần (ví dụ: "oo", "ooo", "oooo",...):
grep -n 'ooo*' regular_express.txt
Mẫu ooo* có nghĩa là "một ký tự 'o', theo sau bởi một ký tự 'o', theo sau bởi KHÔNG hoặc nhiều ký tự 'o'". Do đó, nó khớp với "oo", "ooo", v.v.
Tìm các chuỗi bắt đầu bằng "g", theo sau bởi ít nhất một ký tự "o", và kết thúc bằng "g" (ví dụ: "gog", "goog", "gooog"...):
grep -n 'goo*g' regular_express.txt
Để tìm các dòng bắt đầu bằng "g", kết thúc bằng "g", với BẤT KỲ ký tự nào ở giữa (có hoặc không có):
grep -n 'g.*g' regular_express.txt
Mẫu .* rất phổ biến, nó đại diện cho "không hoặc nhiều ký tự bất kỳ".
Để tìm các dòng chứa ít nhất một số:
grep -n '[0-9][0-9]*' regular_express.txt
Mẫu này có nghĩa là "một chữ số, theo sau bởi không hoặc nhiều chữ số khác".
Giới hạn số lần lặp {}
Bạn có thể chỉ định chính xác số lần lặp lại của một ký tự hoặc nhóm bằng cách sử dụng dấu ngoặc nhọn {}. Vì { và } có ý nghĩa đặc biệt trong shell, bạn cần thoát chúng bằng dấu gạch ngược \.
Cú pháp:
{N}: Lặp lại chính xácNlần.{N,}: Lặp lại ít nhấtNlần.{N,M}: Lặp lại từNđếnMlần.
Tìm các dòng có chuỗi "o" lặp lại chính xác 2 lần ("oo"):
grep -n 'o\{2\}' regular_express.txt
Tìm các chuỗi bắt đầu bằng "g", theo sau bởi "o" lặp lại từ 2 đến 5 lần, và kết thúc bằng "g" (ví dụ: "goog", "gooog", "goooog", "gooooog"):
grep -n 'go\{2,5\}g' regular_express.txt
Tìm các chuỗi bắt đầu bằng "g", theo sau bởi "o" lặp lại ít nhất 2 lần, và kết thúc bằng "g" (ví dụ: "goog", "gooog", "goooog", ...):
grep -n 'go\{2,\}g' regular_express.txt
grep Mở rộng (grep -E hoặc egrep)
egrep hoặc grep -E hỗ trợ một tập hợp các ký tự regex mở rộng, giúp viết các mẫu tìm kiếm phức tạp dễ dàng hơn mà không cần thoát nhiều ký tự.
Các tính năng regex mở rộng
|(OR): Khớp với một trong nhiều mẫu.+: Lặp lại ký tự đứng trước nó từ 1 đến vô số lần.?: Lặp lại ký tự đứng trước nó 0 hoặc 1 lần.(): Nhóm các biểu thức con.
Ví dụ, tìm tất cả các dòng chứa "NW" HOẶC "EA":
egrep 'NW|EA' testfile
Nếu dùng grep tiêu chuẩn, bạn sẽ phải thoát ký tự |: grep 'NW\|EA' testfile.
Tìm các dòng chứa ít nhất một ký tự "3":
egrep '3+' testfile
# Hoặc
grep -E '3+' testfile
# Hoặc với grep tiêu chuẩn, phải thoát '+'
grep '3\+' testfile
Tìm các dòng chứa ký tự "2", theo sau bởi 0 hoặc 1 dấu chấm (.), sau đó là một chữ số từ 0-9:
egrep '2\.?[0-9]' testfile
# Hoặc
grep -E '2\.?[0-9]' testfile
# Hoặc với grep tiêu chuẩn, phải thoát '.' và '?'
grep '2\.\?[0-9]' testfile
Tìm các dòng chứa chuỗi "no" lặp lại một hoặc nhiều lần (ví dụ: "no", "nono", "nonono",...):
egrep '(no)+' testfile
# Hoặc
grep -E '(no)+' testfile
# Hoặc với grep tiêu chuẩn, phải thoát '(', ')' và '+'
grep '\(no\)\+' testfile
Tìm kiếm chuỗi cố định (grep -F hoặc fgrep)
fgrep hoặc grep -F là công cụ lý tưởng khi bạn cần tìm kiếm các chuỗi văn bản cố định mà không muốn chúng bị hiểu là biểu thức chính quy. Điều này làm cho nó nhanh hơn grep thông thường đối với các tác vụ tìm kiếm đơn giản.
Ví dụ, nếu bạn muốn tìm các dòng chứa ký tự dấu sao (*) mà không muốn nó được diễn giải như một ký tự đại diện regex:
fgrep '*' /etc/profile
# Hoặc
grep -F '*' /etc/profile
Lệnh này sẽ tìm chính xác ký tự *, không phải bất kỳ ký tự nào lặp lại 0 hoặc nhiều lần.