103.7 Bài 1
Chứng chỉ: |
LPIC-1 |
---|---|
Phiên bản: |
5.0 |
Chủ đề: |
103 Lệnh GNU và Unix |
Mục tiêu: |
103.7 Tìm kiếm Tệp Văn bản bằng Biểu thức Chính quy |
Bài: |
1 trên 2 |
Giới thiệu
Các thuật toán tìm kiếm theo chuỗi được sử dụng rộng rãi bởi nhiều tác vụ xử lý dữ liệu đến mức các hệ điều hành tương tự như Unix còn có một cách triển khai riêng đã trở nên vô cùng phổ biến là Biểu thức chính quy, thường được viết tắt là REs (Regular expressions). Biểu thức chính quy bao gồm các chuỗi ký tự tạo thành một mẫu chung được sử dụng để định vị và đôi khi là sửa đổi một chuỗi tương ứng trong một chuỗi ký tự lớn hơn. Biểu thức chính quy mở rộng đáng kể các khả năng sau:
-
Viết quy tắc phân tích cú pháp cho các yêu cầu trong máy chủ HTTP, đặc biệt là nginx.
-
Viết các tệp lệnh chuyển đổi bộ dữ liệu dựa trên văn bản sang một định dạng khác.
-
Tìm kiếm các sự kiện được quan tâm trong các mục nhật ký hoặc tài liệu.
-
Lọc tài liệu đánh dấu, giữ nội dung ngữ nghĩa.
Biểu thức chính quy đơn giản nhất chứa ít nhất một nguyên tử. Một nguyên tử (được đặt tên như vậy vì nó là thành phần cơ bản của một biểu thức chính quy) chỉ là một ký tự có thể có hoặc không có ý nghĩa gì đặc biệt. Hầu hết các ký tự thông thường đều khá rõ ràng và sẽ giữ nguyên nghĩa đen, trong khi có một số ký tự có thể mang những ý nghĩa đặc biệt:
.
(dấu chấm):: Nguyên tử khớp với bất kỳ ký tự nào.
^
(dấu mũ):: Nguyên tử khớp với đầu dòng.
$
(dấu đô la):: Nguyên tử khớp với cuối dòng.
Ví dụ: biểu thức chính quy bc
(được tạo bởi các nguyên tử chữ là b
và c
) có thể được tìm thấy trong chuỗi abcd
, nhưng lại không thể được tìm thấy trong chuỗi a1cd
. Mặt khác, ta có thể tìm thấy biểu thức chính quy .c
trong cả hai chuỗi abcd
và a1cd
, vì dấu chấm .
sẽ khớp với bất kỳ ký tự nào.
Các nguyên tử dấu mũ và ký hiệu đô la được sử dụng khi chúng ta chỉ quan tâm tới các kết quả trùng khớp ở đầu hoặc cuối chuỗi. Vì lý do đó, chúng còn được gọi là neo. Ví dụ: cd
có thể được tìm thấy trong abcd
, nhưng ^cd
thì không. Tương tự, ab
có thể được tìm thấy trong abcd
, nhưng ab$
thì không. Dấu mũ ^
là một ký tự chữ trừ khi nó đứng ở đầu và $
là ký tự chữ trừ khi nó đứng ở cuối biểu thức chính quy.
Biểu thức Ngoặc Vuông
Có một loại nguyên tử khác có tên là biểu thức ngoặc vuông. Mặc dù không phải là một ký tự đơn lẻ, dấu ngoặc vuông []
(bao gồm cả nội dung của chúng) vẫn được coi là một nguyên tử. Một biểu thức ngoặc vuông thường chỉ là một danh sách các ký tự chữ được đặt trong []
khiến cho nguyên tử khớp với bất kỳ ký tự đơn nào trong danh sách. Ví dụ: biểu thức [1b]
có thể được tìm thấy trong cả hai chuỗi abcd
và a1cd
. Để chỉ định các ký tự mà nguyên tử không được tương ứng, danh sách phải bắt đầu bằng ^
, như trong [^1b]
. Chúng ta cũng có thể chỉ định phạm vi ký tự trong biểu thức ngoặc. Ví dụ: [0-9]
khớp với các chữ số từ 0 đến 9 và [a-z]
khớp với bất kỳ chữ cái viết thường nào. Phạm vi phải được sử dụng một cách thận trọng vì chúng có thể sẽ không được nhất quán ở tất cả các khu vực.
Các danh sách biểu thức ngoặc vuông cũng chấp nhận các hạng (class) thay vì chỉ các ký tự và phạm vi đơn lẻ. Các hạng ký tự truyền thống là:
[:alnum:]
-
Đại diện cho một ký tự chữ và số.
[:alpha:]
-
Đại diện cho một ký tự chữ cái.
[:ascii:]
-
Đại diện cho một ký tự phù hợp với bộ ký tự ASCII.
[:blank:]
-
Đại diện cho một ký tự trống, nghĩa là một khoảng trắng hoặc một ký tự tab.
[:cntrl:]
-
Đại diện cho một ký tự điều khiển.
[:digit:]
-
Đại diện cho một chữ số (0 đến 9).
[:graph:]
-
Đại diện cho bất kỳ ký tự nào có thể in được ngoại trừ khoảng trắng.
[:lower:]
-
Đại diện cho một ký tự chữ thường.
[:print:]
-
Đại diện cho bất kỳ ký tự nào có thể in, kể cả khoảng trắng.
[:punct:]
-
Đại diện cho bất kỳ ký tự nào có thể in mà không phải là khoảng trắng hoặc ký tự chữ và số.
[:space:]
-
Đại diện cho các ký tự khoảng trắng: dấu cách, nguồn cấp dữ liệu (
\f
), xuống dòng (\n
), dấu xuống dòng (\r
), tab ngang (\t
) và tab dọc (\ v
). [:upper:]
-
Đại diện cho một chữ hoa.
[:xdigit:]
-
Đại diện cho các chữ số thập lục phân (0 đến F).
Các hạng ký tự có thể được kết hợp với các ký tự và phạm vi đơn lẻ nhưng lại không thể được sử dụng làm điểm cuối cho một phạm vi. Ngoài ra, các hạng ký tự chỉ có thể được sử dụng trong các biểu thức ngoặc vuông chứ không phải là một nguyên tử độc lập bên ngoài dấu ngoặc.
Định lượng
Phạm vi tiếp cận của một nguyên tử, nguyên tử ký tự đơn hoặc nguyên tử ngoặc vuông có thể được điều chỉnh bằng cách sử dụng bộ định lượng nguyên tử. Bộ định lượng nguyên tử sẽ xác định các chuỗi nguyên tử - tức các kết quả trùng khớp xảy ra khi một lượt lặp lại liền kề của nguyên tử được tìm thấy trong chuỗi. Chuỗi con tương ứng với kết quả trùng khớp được gọi là các mảnh. Mặc dù vậy, bộ định lượng và các tính năng khác của biểu thức chính quy sẽ được xử lý khác nhau tùy thuộc vào tiêu chuẩn đang được sử dụng.
Theo định nghĩa của POSIX, có hai dạng biểu thức chính quy: biểu thức chính quy “cơ bản” và biểu thức chính quy “mở rộng”. Hầu hết các chương trình liên quan đến văn bản trong bất kỳ bản phân phối Linux thông thường nào cũng đều hỗ trợ cả hai dạng này. Vì vậy, chúng ta phải biết được sự khác biệt giữa chúng để tránh các vấn đề về tương thích và để chọn cách triển khai phù hợp nhất cho tác vụ dự kiến.
Bộ định lượng *
có cùng chức năng trong cả RE cơ bản và RE mở rộng (nguyên tử không xuất hiện hoặc xuất hiện nhiều lần) và nó sẽ là một ký tự chữ nếu xuất hiện ở đầu biểu thức chính quy hoặc trước dấu gạch chéo ngược \
. Bộ định lượng dấu cộng ` sẽ chọn các mảnh có chứa một hoặc nhiều nguyên tử trùng khớp theo chuỗi. Với bộ định lượng dấu chấm hỏi `?`, một kết quả trùng khớp sẽ xảy ra nếu nguyên tử tương ứng xuất hiện một hoặc không lần. Nếu trước dấu gạch chéo ngược `\`, ý nghĩa đặc biệt của chúng sẽ không được xét. Các biểu thức chính quy cơ bản cũng hỗ trợ các bộ định lượng `
và ?
nhưng chúng cần được đặt trước dấu gạch chéo ngược. Không giống như các biểu thức chính quy mở rộng, bản thân +
và ?
là các ký tự chữ trong các biểu thức chính quy cơ bản.
Giới hạn
Giới hạn là một bộ định lượng nguyên tử mà đúng như tên gọi, nó cho phép người dùng chỉ định ranh giới số lượng chính xác cho một nguyên tử. Trong các biểu thức chính quy mở rộng, một giới hạn có thể xuất hiện dưới ba dạng:
{i}
-
Nguyên tử phải xuất hiện đúng
i
lần (i
là một số nguyên). Ví dụ:[[:blank:]]{2}
sẽ khớp với chính xác hai ký tự trống. {i,}
-
Nguyên tử phải xuất hiện ít nhất
i
lần (i
là một số nguyên). Ví dụ:[[:blank:]]{2,}
sẽ khớp với bất kỳ chuỗi nào gồm hai ký tự trống trở lên. {i,j}
-
Nguyên tử phải xuất hiện ít nhất
i
lần và nhiều nhấtj
lần (i
vàj
là các số nguyên,j
lớn hơni
). Ví dụ:xyz{2,4}
sẽ khớp với chuỗixy
theo sau là hai đến bốn ký tựz
.
Trong mọi trường hợp, nếu một chuỗi con khớp với một biểu thức chính quy và một chuỗi con dài hơn bắt đầu từ cùng một điểm cũng khớp thì chuỗi con dài hơn sẽ được xét.
Các biểu thức chính quy cơ bản cũng hỗ trợ các giới hạn, nhưng các dấu phân cách phải được đặt trước \
: \{
và \}
. Bản thân {
và }
được hiểu là các ký tự chữ. Một ký tự \{
được theo sau bởi một ký tự số sẽ được coi là một ký tự chữ chứ không phải là phần đầu của một giới hạn.
Nhánh và Tham chiếu Ngược
Biểu thức chính quy cơ bản cũng khác với biểu thức chính quy mở rộng ở một khía cạnh quan trọng khác: một biểu thức chính quy mở rộng có thể được chia thành nhiều nhánh, mỗi nhánh là một biểu thức chính quy độc lập. Các nhánh được phân tách bằng |
và biểu thức chính quy kết hợp sẽ khớp với bất kỳ mẫu nào tương ứng với bất kỳ nhánh nào. Ví dụ: he|him
sẽ khớp nếu một trong hai chuỗi con he
hoặc him
được tìm thấy trong chuỗi đang được kiểm tra. Các biểu thức chính quy cơ bản diễn giải |
như một ký tự chữ. Tuy nhiên, hầu hết các chương trình hỗ trợ các biểu thức chính quy cơ bản sẽ cho phép các nhánh có chứa \|
.
Một biểu thức chính quy mở rộng được đặt trong ()
có thể được sử dụng trong tham chiếu ngược. Ví dụ: ([[:digit:]])\1
sẽ khớp với bất kỳ biểu thức chính quy nào lặp lại chính nó ít nhất một lần, bởi vì \1
trong biểu thức là tham chiếu ngược tới mảnh được khớp bởi biểu thức con trong dấu ngoặc đơn đầu tiên. Nếu có nhiều hơn một biểu thức con được đặt trong ngoặc đơn tồn tại trong biểu thức chính quy, chúng có thể được tham chiếu bằng \2
, \3
, v.v.
Đối với các RE cơ bản, các biểu thức con phải được bao quanh bởi \(
và \)
(với bản thân (
và )
được coi là các ký tự thông thường). Chỉ số tham chiếu ngược được sử dụng giống như trong các biểu thức chính quy mở rộng.
Tìm kiếm với Biểu thức chính quy
Lợi ích rõ ràng nhất mà biểu thức chính quy mang lại là sự cải thiện trong việc tìm kiếm trên hệ thống tệp và trong tài liệu văn bản. Tùy chọn -regex
của lệnh find
sẽ cho phép ta kiểm tra mọi đường dẫn trong hệ thống phân cấp thư mục dựa trên biểu thức chính quy. Ví dụ,
$ find $HOME -regex '.*/\..*' -size +100M
sẽ tìm kiếm các tệp lớn hơn 100 megabyte (100 đơn vị 1048576 byte) nhưng chỉ trong các đường dẫn bên trong thư mục chính của người dùng và có chứa kết quả trùng khớp với .*/\..*
, tức là có /.
bao quanh bởi bất kỳ một số lượng ký tự nào khác. Nói cách khác, chỉ các tệp ẩn hoặc tệp bên trong các thư mục ẩn mới được liệt kê, bất kể vị trí của /.
là ở đâu trong đường dẫn tương ứng. Thay vào đó, đối với các biểu thức chính quy không phân biệt chữ hoa chữ thường, ta nên sử dụng tùy chọn -iregex
:
$ find /usr/share/fonts -regextype posix-extended -iregex '.*(dejavu|liberation).*sans.*(italic|oblique).*' /usr/share/fonts/dejavu/DejaVuSansCondensed-BoldOblique.ttf /usr/share/fonts/dejavu/DejaVuSansCondensed-Oblique.ttf /usr/share/fonts/dejavu/DejaVuSans-BoldOblique.ttf /usr/share/fonts/dejavu/DejaVuSans-Oblique.ttf /usr/share/fonts/dejavu/DejaVuSansMono-BoldOblique.ttf /usr/share/fonts/dejavu/DejaVuSansMono-Oblique.ttf /usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf /usr/share/fonts/liberation/LiberationSans-Italic.ttf
Trong ví dụ này, biểu thức chính quy chứa các nhánh (được viết theo kiểu mở rộng) để chỉ liệt kê các tệp phông chữ cụ thể trong phân cấp thư mục /usr/share/fonts
. Biểu thức chính quy mở rộng không được hỗ trợ theo mặc định nhưng find
sẽ cho phép kích hoạt chúng với -regextype posix-extended
hoặc -regextype egrep
. Tiêu chuẩn RE mặc định cho find
là findutils-default - gần như là một bản sao của biểu thức chính quy cơ bản.
Thông thường, ta sẽ cần phải truyền đầu ra của chương trình cho lệnh less
khi nó không vừa với màn hình. Lệnh less
sẽ chia đầu vào của nó thành các trang, mỗi trang đều sẽ lấp đầy một màn hình, cho phép người dùng dễ dàng điều hướng văn bản lên và xuống. Ngoài ra, less
cũng cho phép người dùng thực hiện các tìm kiếm dựa trên biểu thức chính quy. Tính năng này đặc biệt quan trọng vì less
là trình phân trang mặc định được sử dụng cho nhiều tác vụ hàng ngày, chẳng hạn như kiểm tra các mục nhật ký hoặc tra cứu các trang hướng dẫn sử dụng. Ví dụ, khi đọc một trang hướng dẫn, việc nhấn phím / sẽ mở ra một dấu nhắc tìm kiếm. Đây là một tình huống điển hình mà trong đó, các biểu thức chính quy rất hữu ích vì các tùy chọn lệnh sẽ được liệt kê ngay sau lề trang trong bố cục trang hướng dẫn chung. Tuy nhiên, cùng một tùy chọn có thể xuất hiện nhiều lần trong văn bản, khiến cho việc tìm kiếm theo nghĩa đen trở nên không khả thi. Dù vậy, việc gõ ^[[:blank:]]*-o
— hoặc đơn giản hơn là ^ *-o
— trong dấu nhắc tìm kiếm vẫn sẽ nhảy ngay đến tùy chọn phần -o
(nếu nó tồn tại) sau khi nhấn Enter, từ đó cho phép người dùng tham khảo mô tả của tùy chọn nhanh hơn.
Bài tập Hướng dẫn
-
Biểu thức chính quy mở rộng nào sẽ khớp với tất cả các địa chỉ email, chẳng hạn như
info@example.org
? -
Biểu thức chính quy mở rộng nào sẽ chỉ khớp với mọi địa chỉ IPv4 ở định dạng tứ giác chấm tiêu chuẩn (như `192.168.15.1)?
-
Làm cách nào để có thể sử dụng lệnh
grep
để liệt kê nội dung của tệp/etc/services
và loại bỏ tất cả các chú thích (dòng bắt đầu bằng#
)? -
Tệp
domains.txt
chứa danh sách các tên miền, mỗi dòng dành cho một tên miền. Lệnhegrep
sẽ được sử dụng như thế nào để chỉ liệt kê các miền.org
hoặc.com
?
Bài tập Mở rộng
-
Từ thư mục hiện tại, lệnh
find
sẽ sử dụng biểu thức chính quy mở rộng như thế nào để tìm kiếm tất cả các tệp không chứa hậu tố tệp tiêu chuẩn (ví dụ: tên tệp không kết thúc bằng.txt
hoặc.c
)? -
Lệnh
less
là lệnh phân trang mặc định để hiển thị các tệp văn bản dài trong môi trường vỏ. Bằng cách nhập/
, một biểu thức chính quy có thể được nhập vào dấu nhắc tìm kiếm để chuyển đến kết quả trùng khớp tương ứng đầu tiên. Để giữ nguyên vị trí tài liệu hiện tại và chỉ đánh dấu các kết quả trùng khớp tương ứng, tổ hợp phím nào cần được nhập tại dấu nhắc tìm kiếm? -
Trong
less
, làm cách nào để có thể lọc đầu ra sao cho chỉ những dòng khớp với biểu thức chính quy mới được hiển thị?
Tóm tắt
Bài học này đã đề cập đến sự hỗ trợ tổng quát của Linux về các biểu thức chính quy - một tiêu chuẩn được sử dụng rộng rãi có khả năng so khớp mẫu được hỗ trợ bởi hầu hết các chương trình liên quan đến văn bản. Bài học đã đi qua các bước sau:
-
Biểu thức chính quy là gì.
-
Các thành phần chính của một biểu thức chính quy.
-
Sự khác nhau giữa biểu thức chính quy thông thường và biểu thức chính quy mở rộng.
-
Cách thực hiện tìm kiếm văn bản và tệp đơn giản bằng các biểu thức chính quy.
Đáp án Bài tập Hướng dẫn
-
Biểu thức chính quy mở rộng nào sẽ khớp với tất cả các địa chỉ email, chẳng hạn như
info@example.org
?egrep "\S+@\S+\.\S+"
-
Biểu thức chính quy mở rộng nào sẽ chỉ khớp với mọi địa chỉ IPv4 ở định dạng tứ giác chấm tiêu chuẩn (như `192.168.15.1)?
egrep "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
-
Làm cách nào để có thể sử dụng lệnh
grep
để liệt kê nội dung của tệp/etc/services
và loại bỏ tất cả các chú thích (dòng bắt đầu bằng#
)?grep -v ^# /etc/services
-
Tệp
domains.txt
chứa danh sách các tên miền, mỗi dòng dành cho một tên miền. Lệnhegrep
sẽ được sử dụng như thế nào để chỉ liệt kê các miền.org
hoặc.com
?egrep ".org$|.com$" domains.txt
Đáp án Bài tập Mở rộng
-
Từ thư mục hiện tại, lệnh
find
sẽ sử dụng biểu thức chính quy mở rộng như thế nào để tìm kiếm tất cả các tệp không chứa hậu tố tệp tiêu chuẩn (ví dụ: tên tệp không kết thúc bằng.txt
hoặc.c
)?find . -type f -regextype egrep -not -regex '.*\.[[:alnum:]]{1,}$'
-
Lệnh
less
là lệnh phân trang mặc định để hiển thị các tệp văn bản dài trong môi trường vỏ. Bằng cách nhập/
, một biểu thức chính quy có thể được nhập vào dấu nhắc tìm kiếm để chuyển đến kết quả trùng khớp tương ứng đầu tiên. Để giữ nguyên vị trí tài liệu hiện tại và chỉ đánh dấu các kết quả trùng khớp tương ứng, tổ hợp phím nào cần được nhập tại dấu nhắc tìm kiếm?Nhấn Ctrl+K trước khi nhập biểu thức tìm kiếm.
-
Trong
less
, làm cách nào để có thể lọc đầu ra sao cho chỉ những dòng khớp với biểu thức chính quy mới được hiển thị?Bằng cách nhấn & và nhập biểu thức tìm kiếm.