Một Regular Expression là một chuỗi mà có thể sử dụng để diễn tả các dãy khác nhau (cách sắp xếp) của các ký tự. Regular Expression thường được sử dụng bởi các lệnh Unix khác nhau, bao gồm ed, sed, awk, grep và miền giới hạn vi. Chương này sẽ hướng dẫn các bạn cách sử dụng Regular Expression cùng với sed. Ở đây sed, là viết tắt của stream editor là một bộ soạn điều hướng chuỗi mà được tạo độc quyền cho các script riêng. Vì thế tất cả những dữ liệu đầu vào mà bạn nhập nó thông qua từ STDOUT và nó không thay đổi file đầu vào.
Gọi sed trong Unix/Linux
Trước khi chúng ta bắt đầu, bảo đảm rằng bạn có một bản sao nội bộ của tệp /etc/passwd để làm việc với sed. Như đã đề cập trước đó, sed có thể được gọi bằng cách gửi dữ liệu thông qua một pipe tới nó như sau:
$ cat /etc/passwd | sed
Usage: sed [OPTION]... {script-other-script} [input-file]...
-n, --quiet, --silent
suppress automatic printing of pattern space
-e script, --expression=script
...............................
Lệnh cat đổ nội dung của /etc/passwd tới sed thông qua pipe trong không gian mẫu của sed. Không gian mẫu là khu vực công việc bên trong mà được sed sử dụng đê thực hiện công việc của nó.
Cú pháp chung của sed trong Unix/Linux
Dưới đây là cú pháp chung cho sed:
/pattern/action
Ở đây, pattern là một Regular Expression, mà action là một trong các lệnh được cung cấp trong bảng sau. Nếu pattern bị bỏ qua, thì action được thực hiện cho mọi dòng như chúng ta đã nhìn ở trên. Ký tự dấu gạch chéo mà bao quanh pattern được yêu cầu bởi vì chúng được sử dụng như là dấu giới hạn.
Dãy | Miêu tả |
---|---|
p | In dòng |
d | Xóa dòng |
s/pattern1/pattern2/ | Thay thế sự kiện của pattern 1 bằng sự kiện trong pattern 2. |
Xóa tất cả các dòng với sed
Gọi sed thêm lần nữa, nhưng lần này nói cho sed sử dụng lệnh chỉnh sửa để xóa các dòng, biểu thị bằng ký tự đơn d:
$ cat /etc/passwd | sed 'd'
$
Thay vì gọi sed bằng cách gửi một file tới nó thông qua một pipe, bạn có thể chỉ dẫn sed để đọc dữ liệu từ một file, như trong ví dụ sau. Lệnh sau thực hiện chính xác công việc tương tự như phần trên đã nhắc đến. Bạn thử thực hành nó mà không sử dụng lệnh cat.
$ sed -e 'd' /etc/passwd
$
Sự định vị SED trong Unix/Linux
Sed cũng hiểu một số thứ gì đó được gọi là các địa chỉ. Địa chỉ hoặc là vị trí riêng trong một file hoặc một dãy mà một lệnh chỉnh sửa riêng nên được áp dụng. Khi sed bắt gặp cái gì đó không được định vị, nó thực hiện các hoạt động của nó trên mọi dòng trong file. Lệnh dưới đây thêm một địa chỉ cơ bản tới lệnh sed mà bạn đang sử dụng:
$ cat /etc/passwd | sed '1d' |more
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
$
Bạn chú ý rằng number 1 được thêm trước lệnh chỉnh sửa xóa. Điều này nói cho sed thực hiện lệnh chỉnh sửa trên dòng đầu tiên của file. Trong ví dụ này, sed sẽ xóa dòng đầu tiên của /etc/passwd và in phần còn lại của file.
Các dãy địa chỉ của sed trong Unix/Linux
Vậy nếu bạn muốn dỡ bỏ nhiều hơn một dòng từ một file thì như thế nào? Bạn có thể xác định một dãy địa chỉ với sed như sau:
$ cat /etc/passwd | sed '1, 5d' |more
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
$
Lệnh trên sẽ áp dụng trên tất cả các dòng từ dòng 1 đến dòng 5. Vì thế nó xóa 5 dòng đầu tiên. Bạn thử thực hành theo các dãy được cung cấp dưới đây:
Dãy | Miêu tả |
---|---|
'4,10d' | Dòng bắt đầu từ 4 đến 10 bị xóa. |
'10,4d' | Chỉ dòng thứ 10 bị xóa, bởi vì sed không làm việc theo hướng nghịch lại. |
'4,+5d' | Nó sẽ so khớp dòng 4 trong file, xóa dòng đó, tiếp tục xóa 5 dòng tiếp theo, và sau đó dừng việc xóa này và in phần còn lại của file. |
'2,5!d' | Nó sẽ xóa mọi thứ trừ từ bắt đầu dòng 2 tới dòng 5. |
'1~3d' | Nó sẽ xóa dòng đầu tiên, bước qua 3 dòng tiếp theo, và sau đó xóa dòng thứ 4. Sed tiếp tục áp dụng mẫu xóa này tới cuối cùng của file. |
'2~2d' | Nó nói cho sed xóa dòng thứ 2, bước qua dòng tiếp theo, xóa dòng tiếp, và lập lại bước trên tới khi file được đọc hết. |
'4,10p' | Bắt đầu từ dòng thứ 4 đến dòng thứ 10 được in. |
'4,d' | Đây là một cú pháp lỗi. |
',10d' | Đây là một cú pháp lỗi. |
Ghi chú: Trong khi sử dụng hành động p, bạn nên sử dụng chức năng -n để tránh lập lại việc in dòng. Bạn thử kiểm tra sự khác nhau giữa hai lệnh sau:
$ cat /etc/passwd | sed -n '1,3p'
Kiểm tra lệnh trên mà không sử dụng chức năng -n như sau:
$ cat /etc/passwd | sed '1,3p'
Lệnh thay thế (substitution command) trong Unix/Linux
Lệnh thay thế, được biểu diễn bởi s, sẽ thay thế bất kỳ chuỗi nào mà bạn xác định với bất cứ chuỗi khác. Để thay thế một chuỗi này với chuỗi khác, bạn cần có một vài cách để nói cho sed nơi chuỗi đầu tiên của bạn kết thúc và chuỗi thay thế bắt đầu. Điều này theo truyền thống được thực hiện bởi việc ngăn cách 2 chuỗi với dấu gạch chéo. Lệnh sau thay thế sự kiện đầu tiên trên một dòng của chuỗi root với chuỗi amrood.
$ cat /etc/passwd | sed 's/root/amrood/'
amrood:x:0:0:root user:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
..........................
Nó là quan trọng để ghi nhớ rằng sed chỉ thay thế sự kiện đầu tiên trên một dòng. Nếu trên chuỗi root diễn ra nhiều hơn một dòng thì chỉ dòng đầu tiên sẽ được thay thế. Để nói cho sed thực hiện thay thế toàn bộ, thêm ký tự g tới phần cuối của lệnh như sau:
$ cat /etc/passwd | sed 's/root/amrood/g'
amrood:x:0:0:amrood user:/amrood:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
...........................
Các cờ hiệu thay thế (Substitution Flag) trong Unix/Linux
Có một số cách sử dụng Flag hữu dụng mà có thể được sử dụng để thêm vào dấu hiệu g, và bạn có thể xác định nó nhiều hơn một lần.
Flag | Miêu tả |
---|---|
g | Thay thế tất cả các so khớp (chuỗi thay thế), không chỉ với so khớp đầu tiên. |
NUMBER | Thay thế chỉ so khớp thứ NUMBER. |
p | Nếu sự thay thế được tạo ra, in không gian mẫu. |
w FILENAME | Nếu sự thay thế được tạo ra, ghi kết quả tới FILENAME. |
I hoặc i | Tạo sự so khớp trong sự phân biệt giữa kiểu chữ (chữ hoa và thường). |
M hoặc m | Trong chế độ xử lý thông thường của các ký tự Regular Expression đặc biệt ^ và $, cờ hiệu này làm cho ^ so khớp với một chuỗi trống sau một dòng mới và $ so khớp với một chuỗi trống trước một dòng mới. |
Sử dụng một chuỗi thay thế trong Unix/Linux
Bạn có thể tự tìm thấy rằng phải thực hiện một sự thay thế trên một chuỗi mà bao gồm ký tự dấu gạch chéo. Trong trường hợp này, bạn có thể xác định một toán tử khác bằng cách cung cấp ký tự chỉ định sau chữ cái s:
$ cat /etc/passwd | sed 's:/root:/amrood:g'
amrood:x:0:0:amrood user:/amrood:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
Trong ví dụ trên chúng ta đã sử dụng dấu hiệu giới hạn : as thay vì dấu gạch chéo / bởi vì chúng ta đang cố gắng tìm kiếm /root thay vì một root đơn.
Đổi vị trí với khoảng trống rỗng trong Unix/Linux
Sử dụng chuỗi thay thế trống để xóa chuỗi root từ tệp /etc/passwd:
$ cat /etc/passwd | sed 's/root//g'
:x:0:0::/:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
Định vị sự thay thế trong Unix/Linux
Nếu bạn muốn thay thế chuỗi sh với chuỗi quiet chỉ trên dòng thứ 10, bạn có thể xác định nó như sau:
$ cat /etc/passwd | sed '10s/sh/quiet/g'
root:x:0:0:root user:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/quiet
Theo cách tương tự, để thay đổi một dãy địa chỉ, bạn có thể thực hiện công việc giống như sau:
$ cat /etc/passwd | sed '1,5s/sh/quiet/g'
root:x:0:0:root user:/root:/bin/quiet
daemon:x:1:1:daemon:/usr/sbin:/bin/quiet
bin:x:2:2:bin:/bin:/bin/quiet
sys:x:3:3:sys:/dev:/bin/quiet
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
Như bạn có thể thấy được từ kết quả đầu ra, 5 dòng đầu tiên của chuỗi sh đã được thay đổi bằng chuỗi quiet, nhưng các dòng còn lại thì không được động chạm đến.
Lệnh so khớp trong Unix/Linux
Bạn sẽ sử dụng chức năng p song song với chức năng -n để in tất cả các dòng so khớp như sau:
$ cat testing | sed -n '/root/p'
root:x:0:0:root user:/root:/bin/sh
[root@ip-72-167-112-17 amrood]# vi testing
root:x:0:0:root user:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
Sử dụng Regular Expression trong Unix/Linux
Trong khi so khớp với mẫu, bạn có thể sử dụng Regular Expression mà cung cấp nhiều tính linh động hơn cho bạn. Kiểm tra ví dụ sau mà so khớp tất cả các dòng bắt đầu với daemon và sau đó xóa chúng:
$ cat testing | sed '/^daemon/d'
root:x:0:0:root user:/root:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
Theo sau là ví dụ mà sẽ xóa tất cả các dòng kết thúc với sh:
$ cat testing | sed '/sh$/d'
sync:x:4:65534:sync:/bin:/bin/sync
Bảng dưới liệt kê 4 ký tự đặc biệt mà rất hữu ích trong các Regular Expression:
Ký tự | Miêu tả |
---|---|
^ | So khớp với phần bắt đầu của các dòng. |
$ | So khớp với phần cuối của các dòng. |
. | So khớp với bất kỳ ký tự đơn nào. |
* | So khớp với 0 hoặc nhiều hơn sự kiện của ký tự trước. |
[chars] | So khớp với bất kỳ ký tự nào được cho trong chars (là một cách bố trí sắp xếp các ký tự). Bạn có thể sử dụng ký tự - để chỉ một dãy của ký tự. |
Các ký tự so khớp trong Unix/Linux
Bạn nhìn vào bảng dưới liệt kê một số Expression khác mà giải thích cho cách sử dụng của các siêu ký tự. Ví dụ, các mẫu sau:
Expression | Miêu tả |
---|---|
/a.c/ | So khớp các dòng mà chứa các chuỗi như a+c, a-c, abc, match và a3c. |
/a*c/ | So khớp với cùng các chuỗi giống với chuỗi đã cho như ace, yacc, và arctic. |
/[tT]he/ | So khớp với chuỗi The và the. |
/^$/ | So khớp với các dòng để trống. |
/^.*$/ | So khớp với một dòng toàn bộ bất kể dòng đó như thế nào. |
/ */ | So khớp với một hoặc nhiều khoảng trống. |
/^$/ | So khớp với các dòng để trống. |
Dưới đây là bảng liệt kê một số bộ bố trí ký tự mà thường được sử dụng:
Thiết lập | Miêu tả |
---|---|
[a-z] | So khớp với một chữ cái đơn thường. |
[A-Z] | So khớp với một chữ cái đơn hoa. |
[a-zA-Z] | So khớp với một chữ cái đơn. |
[0-9] | So khớp với một số đơn. |
[a-zA-Z0-9] | So khớp với một số hoặc một chữ cái đơn. |
Các từ khóa lớp ký tự trong Unix/Linux
Một số từ khóa đặc biệt thường có sẵn cho các Regular Expression, đặc biệt các tiện ích GNU mà sử dụng các Regular Expression. Điều này là rất hữu dụng cho các Regular Expression Sed khi chúng đơn giản hóa các công việc và nâng cao khả năng đọc của nó. Ví dụ, các ký tự từ a đến z cũng như các ký tự từ A đến Z ủy nhiệm một lớp ký tự là các từ khóa [[:alpha:]] Sử dụng từ khóa lớp ký tự chữ cái, lệnh này chỉ in những dòng trong tệp /etc/syslog.conf mà bắt đầu với một ký tự chữ cái.
$ cat /etc/syslog.conf | sed -n '/^[[:alpha:]]/p'
authpriv.* /var/log/secure
mail.* -/var/log/maillog
cron.* /var/log/cron
uucp,news.crit /var/log/spooler
local7.* /var/log/boot.log
Bảng sau là một danh sách đầy đủ của các từ khóa lớp ký tự trong GNU sed.
Lớp ký tự | Miêu tả |
---|---|
[[:alnum:]] | Thuộc chữ cái-số [a-z A-Z 0-9] |
[[:alpha:]] | Bảng chữ cái [a-z A-Z] |
[[:blank:]] | Các ký tự khoảng trống (spaces hoặc tabs) |
[[:cntrl:]] | Các ký tự điều khiển |
[[:digit:]] | Các số [0-9] |
[[:graph:]] | Bất kỳ ký tự nhìn thấy nào (trừ các khoảng trống trắng) |
[[:lower:]] | Các ký tự viết thường [a-z] |
[[:print:]] | Các ký tự có thể in được (các ký tự không phải ký tự điều khiển) |
[[:punct:]] | Các ký tự dấu chấm câu |
[[:space:]] | Khoảng trống trắng |
[[:upper:]] | Các ký tự viết hoa [A-Z] |
[[:xdigit:]] | Các chữ số thập lục phân [0-9 a-f A-F] |
Tham chiếu & trong Unix/Linux
Siêu ký tự & trong sed biểu diễn nội dung của mẫu mà được so khớp. Ví dụ, giả sử bạn có một tệp gọi là phone.txt chứa đầy các số điện thoại, như sau:
5555551212
5555551213
5555551214
6665551215
6665551216
7775551217
Bạn muốn tạo một code khu vực (3 chữ số đầu tiên) được bao quanh bởi dấu ngoặc đơn để dễ dàng hơn khi đọc. Để thực hiện điều này, bạn có thể sử dụng ký tự thay thế &, giống như:
$ sed -e 's/^[[:digit:]][[:digit:]][[:digit:]]/(&)/g' phone.txt
(555)5551212
(555)5551213
(555)5551214
(666)5551215
(666)5551216
(777)5551217
Ở đây trong phần mẫu bạn đang so khớp 3 chữ số đầu tiên, và sau đó sử dụng & bạn đang đổi chỗ cho 3 chữ số này với dấu ngoặc đơn bao quanh.
Sử dụng nhiều lệnh sed trong Unix/Linux
Bạn có thể sử dụng nhiều lệnh sed trong một lệnh sed đơn như sau:
$ sed -e 'command1' -e 'command2' ... -e 'commandN' files
Tại đây, command1 tới commandN là các kiểu lệnh sed đã được bàn luận ở trên. Những lệnh này được áp dụng tới mỗi dòng trong một danh sách các file được cung cấp bởi các file. Sử dụng kỹ thuật tương tự, chúng ta có thể viết ví dụ số điện thoại trên như sau:
$ sed -e 's/^[[:digit:]]\{3\}/(&)/g' \
-e 's/)[[:digit:]]\{3\}/&-/g' phone.txt
(555)555-1212
(555)555-1213
(555)555-1214
(666)555-1215
(666)555-1216
(777)555-1217
Ghi chú: Trong ví dụ trên, thay vì lặp lại các từ khóa lớp ký tự [[:digit:]] 3 lần, bạn đổi nó với \{3\}, mà có nghĩa là để so khớp Regular Expression ở trước 3 lần. Tại đây tôi sử dụng \ để xuống dòng, bạn nên gỡ bỏ nó trước khi chạy lệnh này.
Tham chiếu ngược trong Unix/Linux
Siêu ký tự & là hữu ích, nhưng hữu ích hơn là khả năng định nghĩa các khu vực cụ thể trong một Regular Expression để bạn có thể tham chiếu chúng trong các chuỗi đổi vị trí. Bằng việc định nghĩa các phần cụ thể của một Regular Expression, sau đó bạn có thể xem lại những phần này với một ký tự tham chiếu đặc biệt. Để tham chiếu ngược, đầu tiên bạn phải định nghĩa một khu vực và sau đó xem lại khu vực đó. Để định nghĩa một khu vực, bạn chèn ký tự dấu ngoặc đơn trong dấu chéo ngược quanh mỗi khu vực bạn quan tâm. Khu vực đầu tiên mà bạn bao quanh với dấu chéo ngược sau đó được tham chiếu bởi \1, khu vực thứ hai bởi \2, và tiếp tục. Giả sử phone.txt có các số liệu sau:
(555)555-1212
(555)555-1213
(555)555-1214
(666)555-1215
(666)555-1216
(777)555-1217
Bây giờ bạn thử lệnh sau:
$ cat phone.txt | sed 's/\(.*)\)\(.*-\)\(.*$\)/Area \
code: \1 Second: \2 Third: \3/'
Area code: (555) Second: 555- Third: 1212
Area code: (555) Second: 555- Third: 1213
Area code: (555) Second: 555- Third: 1214
Area code: (666) Second: 555- Third: 1215
Area code: (666) Second: 555- Third: 1216
Area code: (777) Second: 555- Third: 1217
Ghi chú:Trong ví dụ trên, mỗi Regular Expression bên trong dấu ngoặc đơn sẽ được tham chiếu ngược bởi \1, \2, …. Tại đây, tôi sử dụng \ để xuống dòng, bạn nên gỡ bỏ chúng trước khi chạy lệnh.