Cách sử dụng sed để thao tác với văn bản trong môi trường Linux.

Cách sử dụng sed để thao tác với văn bản trong môi trường Linux.

Trình biên tập dòng sed là một công cụ chỉnh sửa mạnh mẽ có thể thực hiện những thay đổi sâu rộng chỉ với rất ít đầu vào. Trong bài viết trước về sed, bạn đã khám phá những kiến thức cơ bản về cách sử dụng sed để chỉnh sửa văn bản. Bài viết này sẽ tiếp tục phần giới thiệu đó bằng cách xem xét một số chủ đề nâng cao hơn.

Lưu ý: Hướng dẫn này sử dụng phiên bản GNU của sed có sẵn trên Ubuntu và các hệ điều hành linux khác. Nếu bạn đang sử dụng macOS, bạn sẽ có phiên bản BSD với các tùy chọn và tham số khác. Bạn có thể cài đặt phiên bản GNU của sed bằng Homebrew với lệnh:

brew install gnu-sed

Để hoàn thành hướng dẫn này, bạn cần một số tệp tin để thao tác, bạn có thể đã có chúng từ bài hướng dẫn đầu tiên. Nếu bạn chưa có, bạn có thể tạo lại chúng với các lệnh sau:

cd
cp /usr/share/common-licenses/BSD .
echo "this is the song that never ends
yes, it goes on and on, my friend
some people started singing it
not knowing what it was
and they'll continue singing it forever
just because..." > song.txt

Ngoài ra, bạn sẽ sử dụng giấy phép GPL 3 trong hướng dẫn này, vì vậy hãy sao chép tệp đó nữa:

cp /usr/share/common-licenses/GPL-3 .

Nếu bạn chưa có, bạn có thể tải xuống bằng lệnh curl:

curl -o GPL-3 https://www.gnu.org/licenses/gpl-3.0.txt

Bây giờ, khi bạn đã có các tệp tin, bạn sẽ khám phá cách sử dụng sed với nhiều lệnh.

Cung cấp nhiều trình tự chỉnh sửa

Có khá nhiều trường hợp bạn có thể muốn truyền nhiều lệnh cho sed cùng lúc. Có một số cách để thực hiện điều này.

Vì sed hoạt động qua luồng đầu vào và đầu ra tiêu chuẩn, bạn có thể nối các lệnh gọi sed khác nhau qua một pipeline. Hãy thực thi lệnh sau để thay thế từ “and” bằng ký tự “&” và từ “people” bằng “horses”:

sed 's/and/\&/' song.txt | sed 's/people/horses/'

Lưu ý rằng bạn cần thoát ký tự “&” bởi vì nó có nghĩa là “toàn bộ mẫu khớp” với sed.

Bạn sẽ thấy kết quả sau:

Output

this is the song that never ends
yes, it goes on & on, my friend
some horses started singing it
not knowing what it was
& they'll continue singing it forever
just because...

Phương pháp này hoạt động, nhưng nó tạo ra overhead không cần thiết với nhiều lệnh gọi sed, đòi hỏi nhiều gõ phím hơn, và không tận dụng được khả năng tích hợp sẵn của sed.

Bạn có thể nối nhiều lệnh cho sed bằng cách sử dụng tùy chọn -e trước mỗi lệnh. Đây là cách viết lại lệnh trên:

sed -e 's/and/\&/' -e 's/people/horses/' song.txt

Một cách khác để nối các lệnh lại với nhau là sử dụng dấu chấm phẩy (;) để phân cách các lệnh riêng biệt. Phương pháp này hoạt động tương tự như ví dụ trên, nhưng không cần dùng -e:

sed 's/and/\&/;s/people/horses/' song.txt

Lưu ý rằng khi sử dụng cú pháp -e, bạn cần nhóm các lệnh trong các cặp dấu nháy đơn riêng biệt. Tuy nhiên, khi phân cách các lệnh bằng dấu chấm phẩy, tất cả các lệnh được đặt trong cùng một chuỗi nháy đơn. Mặc dù hai cách thể hiện các lệnh này đều hữu ích, nhưng có những trường hợp mà kỹ thuật pipeline như ví dụ trước vẫn cần thiết.

Hãy xem xét toán tử =. Toán tử này chèn số dòng vào một dòng mới giữa mỗi dòng hiện có. Kết quả sẽ như sau:

sed '=' song.txt

Bạn sẽ thấy kết quả sau:

Output

1
this is the song that never ends
2
yes, it goes on and on, my friend
3
some people started singing it
4
not knowing what it was
5
and they'll continue singing it forever
6
just because...

Nếu bạn muốn thay đổi định dạng của đánh số bằng cách chỉnh sửa văn bản, bạn sẽ nhận thấy rằng mọi thứ không hoạt động như mong đợi.

Để minh họa, hãy xem lệnh G, theo mặc định, nó chèn một dòng trống giữa mỗi dòng (thực ra phức tạp hơn, nhưng sau này bạn sẽ khám phá):

sed 'G' song.txt

Kết quả sẽ là:

Output

this is the song that never ends

yes, it goes on and on, my friend

some people started singing it

not knowing what it was

and they'll continue singing it forever

just because...

Nếu bạn kết hợp hai lệnh này, bạn có thể mong đợi một khoảng trắng giữa mỗi dòng văn bản và dòng số dòng:

sed '=;G' song.txt

Tuy nhiên, kết quả lại khác:

Output

1
this is the song that never ends

2
yes, it goes on and on, my friend

3
some people started singing it

4
not knowing what it was

. . .
. . .

Điều này xảy ra vì toán tử = chỉnh sửa trực tiếp luồng đầu ra thực tế. Điều này có nghĩa là bạn không thể sử dụng kết quả cho việc chỉnh sửa thêm.

Bạn có thể khắc phục điều này bằng cách sử dụng hai lệnh sed, coi như sự thay đổi đầu tiên của sed là một luồng văn bản đơn giản cho lệnh thứ hai:

sed '=' song.txt | sed 'G'

Bây giờ, bạn sẽ thấy kết quả như mong đợi:

Output

1

this is the song that never ends

2

yes, it goes on and on, my friend

3

some people started singing it
. . .
. . .

Hãy nhớ rằng một số lệnh hoạt động theo cách này, đặc biệt nếu bạn nối nhiều lệnh với nhau và đầu ra khác với những gì bạn mong đợi.

Địa chỉ nâng cao

Một trong những ưu điểm của các lệnh có địa chỉ trong sed là bạn có thể sử dụng biểu thức chính quy làm tiêu chí lựa chọn. Điều này có nghĩa là bạn không bị giới hạn chỉ thao tác trên các dòng có số thứ tự xác định như bạn đã thấy trước đó:

sed '1,3s/.*/Hello/' song.txt

Output

Hello
Hello
Hello
not knowing what it was
and they'll continue singing it forever
just because...

Thay vào đó, bạn có thể sử dụng biểu thức chính quy để chỉ thao tác trên các dòng chứa một mẫu nhất định. Để làm điều này, đặt mẫu cần tìm giữa hai dấu gạch chéo (/) trước khi đưa ra chuỗi lệnh:

sed '/singing/s/it/& loudly/' song.txt

Output

this is the song that never ends
yes, it goes on and on, my friend
some people started singing it loudly
not knowing what it was
and they'll continue singing it loudly forever
just because...

Trong ví dụ này, bạn đã chèn từ “loudly” sau lần xuất hiện đầu tiên của “it” trên mỗi dòng có chứa chuỗi “singing”. Lưu ý rằng dòng thứ hai và thứ tư không bị thay đổi vì chúng không khớp với mẫu.

Các biểu thức địa chỉ có thể phức tạp tùy ý, tạo ra sự linh hoạt lớn trong việc thực hiện các lệnh.

Ví dụ không phức tạp này minh họa cách sử dụng biểu thức chính quy để tạo địa chỉ cho các lệnh khác. Lệnh sau khớp với tất cả các dòng trống (dòng bắt đầu ngay và kết thúc ngay) và truyền chúng cho lệnh xóa:

sed '/^$/d' GPL-3

Kết quả bạn sẽ thấy:

Output

                  GNU GENERAL PUBLIC LICENSE
                      Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. 
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
                          Preamble
The GNU General Public License is a free, copyleft license for
. . .
. . .

Hãy nhớ rằng bạn có thể sử dụng biểu thức chính quy ở cả hai bên của một phạm vi. Ví dụ, bạn có thể xóa các dòng bắt đầu từ một dòng chỉ chứa từ “START” đến một dòng chứa “END”.

Ví dụ, tạo một tệp tên là inputfile:

echo "This is an input file
START
this is the text we don't want
END
This is additional text" > inputfile

Bây giờ, sử dụng sed để xóa nội dung giữa “START” và “END”:

sed '/^START$/,/^END$/d' inputfile

Kết quả sẽ là:

Output

This is an input file
This is additional text

Một điều cần lưu ý là lệnh này sẽ xóa mọi thứ từ “START” đầu tiên đến “END” đầu tiên, và sau đó bắt đầu lại quá trình xóa nếu gặp thêm một dấu hiệu “START” khác.

Nếu bạn muốn đảo ngược địa chỉ (thao tác trên mọi dòng không khớp với mẫu), bạn có thể theo sau mẫu bằng dấu chấm than (!).

Ví dụ, bạn có thể xóa mọi dòng không trống (không quá hữu ích, nhưng chỉ để minh họa), với lệnh sau:

sed '/^$/!d' GPL-3

Kết quả sẽ là một đầu ra chứa nhiều dòng trống, vì sed mặc định vẫn in các dòng:

Output


Địa chỉ không cần phải là biểu thức phức tạp để đảo ngược. Phép đảo ngược cũng hoạt động với các địa chỉ số thứ tự.

Sử dụng bộ nhớ giữ

Một tính năng giúp tăng cường khả năng chỉnh sửa trên nhiều dòng của sed là “bộ nhớ giữ” (hold buffer). Bộ nhớ giữ là một vùng lưu trữ tạm thời có thể được thay đổi bởi một số lệnh nhất định.

Sự có mặt của bộ nhớ phụ này có nghĩa là bạn có thể lưu các dòng trong khi thao tác trên các dòng khác, và sau đó xử lý các dòng được lưu theo nhu cầu.

Các lệnh ảnh hưởng đến bộ nhớ giữ như sau:

  • h: Sao chép bộ nhớ mẫu hiện tại (dòng bạn đang khớp và làm việc) vào bộ nhớ giữ (lệnh này xóa nội dung trước đó của bộ nhớ giữ).
  • H: Nối bộ nhớ mẫu hiện tại vào cuối bộ nhớ giữ hiện tại, phân cách bởi ký tự xuống dòng (\n).
  • g: Sao chép bộ nhớ giữ hiện tại vào bộ nhớ mẫu. Bộ nhớ mẫu trước đó bị xóa.
  • G: Nối bộ nhớ giữ hiện tại vào cuối bộ nhớ mẫu hiện tại, phân cách bởi ký tự xuống dòng (\n).
  • x: Đổi chỗ bộ nhớ mẫu và bộ nhớ giữ.

Nội dung của bộ nhớ giữ không thể được thao tác cho đến khi nó được chuyển sang bộ nhớ mẫu bằng một cách nào đó.

Hãy cùng khám phá ý tưởng này với một ví dụ phức tạp.

Đây là một ví dụ theo quy trình để nối các dòng liền kề (sed thực ra có lệnh tích hợp để thực hiện điều này, lệnh N nối dòng tiếp theo vào dòng hiện tại. Tuy nhiên, bạn sẽ làm theo cách “khó” để rèn luyện kỹ năng):

sed -n '1~2h;2~2{H;g;s/\n/ /;p}' song.txt

Kết quả bạn sẽ thấy:

Output

this is the song that never ends yes, it goes on and on, my friend
some people started singing it not knowing what it was
and they'll continue singing it forever just because...

Điều này có nhiều điều cần ghi nhớ, vì vậy hãy cùng phân tích:

  • Phần đầu tiên cần lưu ý là tùy chọn -n được sử dụng để tắt in tự động. Sed sẽ chỉ in khi bạn chỉ định cụ thể.
  • Phần đầu tiên của lệnh là 1~2h. Địa chỉ ở đây có nghĩa là thực hiện lệnh trên dòng đầu tiên và sau đó trên mỗi dòng lẻ tiếp theo. Lệnh h sao chép dòng khớp vào bộ nhớ giữ.
  • Phần thứ hai của lệnh phức tạp hơn. Lại bắt đầu với địa chỉ, lần này là các dòng chẵn (ngược lại với lệnh đầu tiên).
  • Phần còn lại của lệnh được bao bọc trong dấu ngoặc nhọn, nghĩa là các lệnh bên trong sẽ thừa hưởng địa chỉ đã chỉ định. Nếu không có dấu ngoặc nhọn, chỉ lệnh “H” mới thừa hưởng địa chỉ, các lệnh còn lại sẽ được thực thi trên mọi dòng.
  • Lệnh H sao chép ký tự xuống dòng và dòng hiện tại vào cuối bộ nhớ giữ hiện tại.
  • Bộ nhớ giữ (gồm dòng lẻ, ký tự xuống dòng, sau đó là dòng chẵn) được sao chép lại vào bộ nhớ mẫu (thay thế nội dung cũ) bằng lệnh g.
  • Cuối cùng, ký tự xuống dòng được thay thế bằng khoảng trắng và dòng được in ra bằng lệnh p.

Nếu bạn tò mò, sử dụng lệnh N sẽ rút gọn đoạn mã đáng kể. Lệnh sau sẽ cho kết quả giống như bạn vừa thấy:

sed -n 'N;s/\n/ /p' song.txt

Sử dụng scripts

Khi bạn bắt đầu sử dụng các lệnh phức tạp hơn, có thể hữu ích khi soạn các lệnh này trong một trình soạn thảo văn bản. Điều này cũng hữu ích nếu bạn có một số lượng lớn các lệnh muốn áp dụng cho một tệp tin.

Ví dụ, nếu bạn thích soạn tin nhắn bằng văn bản thuần, nhưng cần thực hiện một bộ định dạng tiêu chuẩn trước khi sử dụng văn bản đó, một script sed sẽ rất hữu ích.

Thay vì nhập từng lệnh sed, bạn có thể đưa các lệnh vào một script và cung cấp nó như một đối số cho sed. Một script sed đơn giản chỉ là danh sách các lệnh sed thô (phần mà thường được đặt giữa các dấu nháy đơn).

Để thử nghiệm, tạo một tệp mới có tên là sed_script với nội dung sau:

echo "s/this/that/g
s/people/horses/g
1,5s/it/that/g" > sed_script

Lưu tệp và thoát trình soạn thảo.

Bây giờ, chỉ dẫn cho sed sử dụng tệp script bằng cách dùng tùy chọn -f:

sed -f sed_script song.txt

Kết quả sẽ như sau:

Output

that is the song that never ends
yes, that goes on and on, my friend
some horses started singing that
not knowing what that was
and they'll continue singing that forever
just because...

Điều này cho phép bạn đưa tất cả các chỉnh sửa vào một tệp và áp dụng chúng cho các tệp văn bản cần tuân theo định dạng bạn đã tạo.

Kết luận

Các lệnh của sed ban đầu không dễ hiểu, và bạn cần phải thực hành thực nghiệm để nắm bắt được tính hữu ích của chúng. Vì lý do này, bạn nên luyện tập thao tác trên văn bản trước khi thực sự cần đến chúng. Hãy có một mục tiêu rõ ràng và cố gắng thực hiện chỉ với sed.

Hy vọng, đến thời điểm này, bạn đã bắt đầu hiểu được sức mạnh mà việc thành thạo sed mang lại. Càng quen thuộc với sed, bạn sẽ phải làm việc ít hơn về lâu dài.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *