2.3. Essential Commands: Tìm file, xử lý text stream và vi
Mục lục
- 1. Khi nào cần find và cú pháp của nó
- 2. Tìm theo mốc thời gian
- 3. Tìm theo kích thước
- 4. Kết hợp nhiều điều kiện trong một search expression
- 5. Tìm theo permission
- 6. Xem nội dung file text
- 7. Biến đổi text stream với sed cut sort và uniq
- 8. So sánh file với diff và sdiff
- 9. Pager và trình soạn thảo vi
- Nguồn tham khảo
1. Khi nào cần find và cú pháp của nó
Quen Linux một thời gian sẽ thấy file được tổ chức khá gọn gàng. Cần cấu hình SSH daemon thì biết ngay config file nằm ở /etc/ssh. Cần xem lỗi đã ghi log thì vào /var/log. Phần lớn thời gian đều biết mọi thứ nằm đâu, ít nhất là biết đại khái. Vậy tìm file để làm gì?
Vài tình huống điển hình:
- Có một website và cần liệt kê toàn bộ file ảnh của nó. Nếu directory của website là
/usr/share, lấy danh sách mọi file.jpgrất nhanh. - Server gần hết disk space. Server này đang host các virtual machine, và quan sát thấy phần lớn virtual machine chỉ cần file dưới 20 gig. Vậy tìm file lớn hơn 20 gig để lọc ra những cái bất thường.
- Vừa update một application và muốn biết file nào đã bị thay đổi. Liệt kê nhanh mọi file được sửa trong một phút vừa rồi.
- Cần biết đội quản trị đã sửa những configuration file nào trong một giờ qua.
Lệnh làm việc này là find.
Cú pháp dùng xuyên suốt phần này: tìm file tên file1.txt trong directory /bin.
find /bin -name file1.txt
-name là một search parameter, chỉ định tên file cần tìm. Phần path có thể bỏ qua, khi đó find tìm trong current directory.
Vài lần đầu dùng lệnh này rất dễ lẫn vị trí của directory path với các search parameter, kiểu viết thành find -name file1.txt /bin.
💡 Hình dung: trước tiên phải đi tới đó, rồi mới tìm được. Phải bước vào phòng đã, sau đó mới tìm chùm chìa khóa. Thứ tự là vị trí tìm trước, điều kiện tìm sau.
Tìm theo tên
-name phân biệt chữ hoa chữ thường, nên nó không tìm ra file tên Filex nếu pattern viết là filex. Muốn bỏ qua phân biệt hoa thường thì thêm chữ i vào trước tên option, thành -iname:
find -name filex
find -iname filex
Khi cần tìm nhiều file cùng chung một pattern trong tên, ví dụ mọi file bắt đầu bằng chữ f thường, dùng wildcard expression: phần đầu của biểu thức rồi một dấu sao. Dấu sao là quân joker của text, khớp với bất cứ thứ gì, kể cả zero ký tự hay một trăm ký tự:
find -name f*
Lấy lại ví dụ ở đầu mục, liệt kê mọi file .jpg trong directory của website:
find /usr/share -name *.jpg
2. Tìm theo mốc thời gian
Liệt kê file được sửa trong một phút vừa rồi:
find -mmin -1
Để nhớ mmin, nghĩ tới modified minute. Nhưng dấu - trước số có ý nghĩa riêng: 1, -1 và +1 cho ba kết quả khác nhau, và cách find làm việc với timestamp không trực quan như trông đợi.
Giả sử lúc này là 12:05.
Với -mmin 5, kết quả là file được tạo đúng vào 12:01, không phải 12:00 như trông đợi. Lý do: khi lùi từ thời điểm hiện tại, hệ thống bước qua 12:05, 12:04, 12:03, 12:02 và dừng ở 12:01, đúng năm giá trị.
Với -mmin -5, kết quả là file được sửa trong năm phút vừa rồi, tức các file sửa vào 12:05, 12:04, 12:03, 12:02 và 12:01. 12:00 không được tính, vì từ 12:05 về 12:01 là đúng năm giá trị; tính tới 12:00 là sáu giá trị, nên phải nâng lên -mmin -6 mới bắt được timestamp đó.
Với -mmin +5, kết quả là file từ 12:00 trở về trước: 12:00, 11:59, 11:58 và xa hơn. Lần này có một chỗ lật ngược: thấy 12:00 thay vì 12:01, vì khi dùng dấu cộng, phép đếm bỏ qua thời điểm hiện tại nên nó bắt đầu đếm từ 12:04 chứ không phải 12:05. Cách nghĩ khác về -mmin +5: nó cho ra mọi object được tạo hơn năm phút trước; 12:01 không phải hơn năm phút trước mà là đúng năm phút trước, nên hơn năm phút nghĩa là 12:00 hoặc sớm hơn.
mtime và đơn vị 24 giờ
-mtime giống -mmin, chỉ khác đơn vị: thay vì phút, nó làm việc theo khối 24 giờ:
find -mtime 0
find -mtime 1
-mtime 0 khớp những gì được sửa trong 24 giờ vừa rồi. -mtime 1 khớp những gì được sửa trong khoảng từ 24 tới 48 giờ trước, và tiếp tục như vậy.
Modify time và change time
Chữ "modification" ở đây có hai nghĩa: file được edit lần cuối, mà cũng có thể là file được tạo ra. Edit một file text lúc 12:05 thì nó hiện modified lúc 12:05. Nhưng tạo một file mới lúc 12:06 cũng được tính là một modification, nên file đó hiện modified lúc 12:06 dù đó là lúc nó được tạo.
Linux còn có khái niệm change time, nghe như trùng với modify time nhưng khác hẳn. Modify time nói về thời điểm nội dung bị sửa. Change time nói về thời điểm metadata bị sửa, mà metadata là dữ liệu về dữ liệu: ở đây là thông tin về file, ví dụ file permission.
Chỗ này hữu dụng trong một tình huống cụ thể: application đột nhiên báo lỗi và nghi ngờ có ai đó đổi sai permission của file nào đó. Tìm file có permission bị đổi trong năm phút vừa rồi:
find -cmin -5
3. Tìm theo kích thước
Tìm file có kích thước đúng 512 KB:
find -size 512k
Chữ k ở đây nghĩa là kilobyte, và còn ba hậu tố khác dùng được:
| Hậu tố | Đơn vị |
|---|---|
c | byte |
k | kilobyte |
M | megabyte |
G | gigabyte |
Hai hậu tố M và G bắt buộc phải viết hoa.
Tìm file lớn hơn 512 KB thì dùng +512k, tìm file nhỏ hơn 512 KB thì dùng -512k:
find -size +512k
find -size -512k
Quay lại tình huống server gần hết disk ở mục đầu. Trên máy lab không có file lớn 20 gig để thử, nhưng cách tìm file lớn hơn 10 megabyte thì như sau:
find -size +10M
4. Kết hợp nhiều điều kiện trong một search expression
-size 512k và -name f* đều là search parameter. Trong manual của lệnh, phần nói cho find biết phải tìm cái gì được gọi là search expression, vì giống như trong toán, nhiều parameter kết hợp lại được thành một biểu thức phức tạp hơn.
Toán tử AND ngầm
Muốn tìm file bắt đầu bằng chữ f và đồng thời có kích thước đúng 512 KB thì liệt kê cả hai parameter cạnh nhau:
find -name f* -size 512k
Viết như vậy nghĩa là có một toán tử AND ngầm giữa -name và -size: cả hai điều kiện phải đúng thì file mới xuất hiện trong kết quả.
Toán tử OR
Muốn OR thay vì AND, tức file bắt đầu bằng chữ f hoặc file có kích thước đúng 512 KB, không cần thỏa cả hai cùng lúc, thì chèn -o vào đúng chỗ muốn đặt toán tử OR:
find -name f* -o -size 512k
Toán tử NOT
Tìm mọi file không bắt đầu bằng chữ f: đặt -not trước -name:
find -not -name f*
Kết quả là danh sách tên file không bắt đầu bằng chữ f. Toán tử NOT còn một cách viết nữa là dấu chấm than. Nhưng với Bash, dấu chấm than có ý nghĩa đặc biệt: thấy ký tự đó trong một lệnh, Bash sẽ hiểu theo cách riêng của nó, tưởng rằng người dùng muốn thực hiện một hành động đặc biệt ở vị trí đó.
Để bảo Bash bỏ qua ý nghĩa đặc biệt của ký tự và coi nó là ký tự thường thì escape nó, bằng cách thêm một dấu backslash phía trước:
find \! -name f*
5. Tìm theo permission
find lọc được theo permission của file và directory. Lấy notation octal 664 làm ví dụ: user đọc và ghi được, group đọc và ghi được, other đọc được.
Khớp chính xác
find -perm 664
Lệnh này tìm file có đúng bộ permission đó, không hơn không kém.
Tối thiểu phải có
Thêm một dấu - trước con số thì điều kiện thành "có ít nhất những permission này":
find -perm -664
File có thêm permission khác vẫn xuất hiện trong kết quả, nhưng file có ít hơn thì không. Hiểu theo kiểu "đây là bộ permission tối thiểu". Ví dụ 664 quy định user phải có cả read và write; user chỉ có read mà không có write thì find không đưa file đó vào kết quả.
Khớp bất kỳ bit nào
Dùng dấu / để tìm file có bất kỳ permission nào trong bộ đó:
find -perm /664
Khác với điều kiện tối thiểu ở trên, dạng này bao trùm rộng hơn. Nếu một user đọc được file nhưng không ghi được, file vẫn xuất hiện trong kết quả vì đã khớp một permission, tức u=r. Các permission còn lại có hay không không quan trọng.
Cả ba dạng trên còn viết được bằng ký hiệu chữ thay cho octal, dạng u=r, giống cách chmod dùng chữ ở Part 2.2: Phân quyền file và permission đặc biệt.
Bốn ví dụ đối chiếu
Giả sử có một nhóm file tên filex, James, Bob, Freya, Jacob, John và Jean.
Tìm file mà chỉ owner đọc và ghi được, không ai khác có quyền gì:
find -perm 600
# Output (minh họa):
./filex
./James
./Bob
Tìm file mà owner tối thiểu phải thực thi được, còn các permission khác thế nào cũng được:
find -perm /100
# Output (minh họa):
./Freya
./Jacob
Tìm file mà other không đọc được, dùng toán tử NOT:
find -not -perm /004
# Output (minh họa):
./filex
./James
./Bob
./Freya
./John
./Jean
Tìm file mà ít nhất một trong ba nhóm đọc được, không quan trọng là nhóm nào:
find -perm /444
Trong ví dụ này mọi file đều khớp điều kiện cuối. File nào không ai đọc được thì không xuất hiện trong kết quả.
6. Xem nội dung file text
Làm việc với Linux là làm việc với rất nhiều text. Phiên SSH toàn là text, output là text, lệnh gõ vào là text, và cấu hình application hay cấu hình chính hệ thống cũng qua file text. Vậy phải biết cách xem, sửa, biến đổi và so sánh file text.
File nhỏ thì dùng cat kèm tên file cần xem:
cat /home/users.txt
Xem file theo chiều ngược, từ dưới lên, thì dùng tac:
tac /home/users.txt
File rất dài thì khác. Trường hợp hay gặp là log file, nơi một program ghi lại mọi status message về việc nó đã làm gì, cái gì thành công, cái gì thất bại. Với log, thường chỉ cần xem chuyện gì xảy ra trong vài giây hay vài phút cuối, và những dòng đó nằm ở cuối file, phần gọi là tail:
tail /home/users.txt
Mặc định tail hiện 10 dòng cuối. Chỉ định số dòng muốn xem bằng option -n:
tail -n 3 /home/users.txt
Lệnh ngược lại, xem mấy dòng đầu thay vì mấy dòng cuối, là head, cũng dùng -n để chỉ định số dòng tính từ đầu file:
head -n 3 /home/users.txt
Một chi tiết dễ đếm sai: file ví dụ có 9 dòng text, nhưng dòng đầu tiên trong log là một dòng trống, nên tổng số dòng thực tế là 10.
7. Biến đổi text stream với sed cut sort và uniq
Có lúc phải xử lý file lớn cần sửa cùng một chỗ ở rất nhiều dòng. Sửa tay thì mệt, nên có những công cụ tự động hoá việc đó.
sed để search and replace
Giả sử có một file chứa danh sách người, kèm địa chỉ, số điện thoại và quốc tịch. Chữ Canada bị viết sai ở một số chỗ thành Canda, thiếu một chữ a ở giữa, và cần thay mọi chỗ Canda thành Canada.
Công cụ làm việc này là sed, tên đến từ stream editor: một dòng text đi vào utility, bị biến đổi theo cách nào đó, rồi utility xuất ra dòng text đã sửa.
sed 's/Canda/Canada/g' userinfo.txt
Lệnh này xem trước sed sẽ làm gì mà chưa sửa file thật, và nên xem trước như vậy để chắc chắn không có sai sót. Bóc tách từng phần:
- Phần cuối là tên file, hoặc path tới file nếu nó không ở current directory.
- Phần
s/Canda/Canada/glà nơi mọi việc diễn ra. Nó phải được bọc trong dấu nháy đơn để command interpreter không can thiệp vào nội dung bên trong. Pattern có thể chứa ký tự đặc biệt như dấu sao; không bọc nháy đơn thì Bash sẽ biến dấu sao đó thành thứ khác, vì Bash hiểu dấu sao theo một nghĩa đặc biệt. Bọc nháy đơn là cách nói với Bash rằng đừng diễn giải gì trong khoảng đó. - Chữ
sđầu tiên chosedbiết đây là lệnh substitute, tức search and replace. - Chữ
gcuối cùng là global.
Mặc định sed chỉ thay thứ đầu tiên nó tìm thấy trên mỗi dòng. Bỏ g đi và chạy lại sẽ thấy chỉ chỗ Canda đầu tiên trên mỗi dòng được đổi thành Canada, các chỗ còn lại trên cùng dòng giữ nguyên. Thêm g là bảo nó thay mọi chỗ khớp, kể cả nhiều chỗ trên một dòng.
Tới đây sed chỉ in ra kết quả dự kiến, chưa sửa file. Chạy cat lên file sẽ thấy nội dung vẫn như cũ. Khi đã hài lòng với bản xem trước thì thêm option -i để nó sửa thật:
sed -i 's/Canda/Canada/g' userinfo.txt
cat userinfo.txt
-i viết tắt của in place, sửa trực tiếp trong file. Lệnh chạy xong không in gì ra command line, nhưng cat sẽ cho thấy thay đổi đã nằm trong file.
cut để lấy đúng cột cần
Muốn lấy riêng phần tên, tức cột đầu tiên của file, thì dùng cut. Lệnh này bóc ra đúng những phần cần lấy từ một file:
cut -f1 -d' ' userinfo.txt
-f chỉ định field muốn lấy, ở đây mỗi từ là một field và cần field một, tức từ đầu tiên của mỗi dòng. Các từ được phân cách bằng dấu cách, nên dấu cách bọc trong nháy đơn được đưa vào làm delimiter.
Delimiter có thể là bất cứ ký tự nào. Với những số như 10.22 hay 12.34 mà chỉ cần phần nguyên, dùng dấu chấm làm delimiter là lấy ra được 10 và 12.
Ví dụ khác: một file phân cách các entry bằng dấu phẩy, và cần lấy toàn bộ tên quốc gia, nằm ở field thứ ba. Thêm yêu cầu nữa là ghi output vào một file mới tên countries.txt:
cut -d, -f3 userinfo.txt > countries.txt
cat countries.txt
Dấu > là một redirection, nó lưu output vào file. Chi tiết về redirection nằm ở Part 2.4: grep, regular expression, archive và I/O redirection; ở đây chỉ cần biết nó ghi output ra file.
sort và uniq
File countries.txt giờ là danh sách quốc gia, nhưng có trùng lặp. Cần danh sách quốc gia không trùng để biết tổng số quốc gia khác nhau mà các user đến từ đó:
uniq countries.txt
Output vẫn còn những dòng trùng. Lý do: uniq chỉ bỏ những dòng trùng nằm cạnh nhau. Nó đã bỏ hai dòng Canada nằm liền nhau ở cuối, nhưng không bỏ những dòng trùng cách xa nhau.
Vậy phải làm cho các dòng giống nhau nằm kề nhau, và công cụ đó là sort. Lệnh này sắp các entry trong file theo thứ tự chữ và số, nên các quốc gia cùng tên sẽ nằm sát nhau. Sau đó đưa output của sort qua uniq:
sort countries.txt | uniq
Dấu | là piping: nó chuyển output của lệnh này làm input cho lệnh kia. Lệnh trên cho ra đúng kết quả cần tìm.
8. So sánh file với diff và sdiff
Một đợt upgrade vừa đổi một configuration file cũ. File cũ có 250 dòng text, file mới có 268 dòng. Mở cả hai ra rồi đọc từng dòng để so là việc rất khó làm.
Giả sử hai file có dòng một và dòng bốn khác nhau, còn dòng hai và dòng ba giống nhau ở cả hai file:
diff file1 file2
# Output (minh họa):
1c1
< first line in file one
---
> first line in file two
4c4
< fourth line in file one
---
> fourth line in file two
diff chỉ quan tâm điểm khác nhau, nên những dòng giống nhau không hiện ra. Cách đọc output:
1c1nghĩa là dòng một của file một bị thay đổi (chữclà changed) ở dòng một của file hai. Nói gọn là dòng một của file một khác dòng một của file hai.- Dấu
<cho biết nội dung này nằm ở file thứ nhất, file bên trái. - Dấu
>cho biết nội dung này nằm ở file thứ hai, file bên phải.
Với file lớn thì không biết những dòng đó nằm ở đâu trong file, nên có thể yêu cầu diff cho thêm ngữ cảnh: hiện cả đoạn text xung quanh vùng khác nhau và vị trí của chúng. Option là -c:
diff -c file1 file2
Ở chế độ này, những dòng khác nhau được đánh dấu bằng dấu chấm than, dấu cộng hoặc dấu trừ, còn những dòng giống nhau không có dấu gì. Dấu chấm than báo hai dòng khác nhau, dấu cộng và dấu trừ báo nội dung được thêm vào hoặc bị bỏ đi.
Cách dễ nhìn ra khác biệt nhất là so sánh cạnh nhau, với option -y:
diff -y file1 file2
Một lệnh tương đương dễ nhớ hơn là sdiff. Nghĩ tới chữ side-by-side diff là nhớ ra tên lệnh:
sdiff file1 file2
Ở hai chế độ cạnh nhau này, điểm khác biệt được đánh dấu bằng ký hiệu dấu gạch dọc.
9. Pager và trình soạn thảo vi
Pager là những program cho phép mở nhiều trang text và di chuyển qua chúng ngay trong terminal. Hai pager cần biết là less và more. less có nhiều tính năng hơn more.
less
Gõ less rồi tên file muốn mở. Ví dụ với file syslog vì nó có rất nhiều dòng:
sudo less /var/log/syslog
Cách nhận ra mình đang ở trong một pager: góc dưới bên trái hiện tên file được highlight. Biết dấu hiệu này có ích, vì đôi khi program khác cũng đẩy người dùng vào một pager mà không báo trước.
Trong less:
- Phím mũi tên di chuyển lên xuống trong file.
- Nhấn phím
/để mở ô tìm kiếm ở góc dưới bên trái, nhập từ cần tìm. Mọi chỗ xuất hiện từ đó đều được highlight. - Nhấn
nđể nhảy tới chỗ xuất hiện tiếp theo. - Mặc định tìm kiếm phân biệt hoa thường, nên một số kết quả bị bỏ qua. Truyền option
-iđể bỏ qua phân biệt hoa thường, khi đó mọi chỗ xuất hiện đều được bắt. - Giữ Shift rồi nhấn
n, tứcNhoa, để lùi về các kết quả trước. - Nhấn
qđể thoát pager.
more
more ít tính năng hơn, nhưng mở theo cùng cách: gõ more rồi tên file muốn đọc.
more /var/log/syslog
Dấu hiệu nhận biết là dòng dưới cùng hiện chữ More kèm một tỷ lệ phần trăm. Di chuyển bằng Spacebar, mỗi lần nhấn là một trang, và phần trăm cho biết đã đi được bao nhiêu phần của file. Thoát bằng q.
vi
vim viết tắt của Vi Improved. Mở lần đầu, nó hiện một màn hình chào kèm vài việc làm được với nó và vài lệnh dùng được.
vim phân biệt mode: nó có nhiều mode khác nhau, và phím cùng chức năng dùng được tùy mode đang ở.
Muốn ghi text vào file thì nhấn i để vào Insert mode. Góc dưới bên trái hiện chữ insert, và từ lúc đó gõ gì vào file nhận cái đó. Nhấn Escape để quay về mode mặc định, tức Command mode.
Tạo file mới:
vim test.txt
Lệnh trên tạo file mới và mở nó ra. Muốn lưu nội dung thì nhấn Escape về command mode rồi gõ :w để write vào file. Thoát chương trình, câu hỏi kinh điển trên internet, làm như sau: nhấn Escape, rồi :wq cho write and quit.
Một mẹo hữu ích nữa: mở lại file, nhập một mớ nội dung rồi quyết định không muốn lưu, muốn bắt đầu lại từ đầu. Thoát vi mà không ghi:
Escape, sau đó :q!
Mở một file đã tồn tại thì gõ vim kèm tên file. Trong file, di chuyển bằng phím mũi tên, hoặc nhấn l để sang phải, h để sang trái, k để lên, j để xuống. Có nhiều cách di chuyển và không cần chuột.
Tìm kiếm cũng dùng phím / như trong less, và thực hiện ở mode mặc định. Mặc định tìm kiếm phân biệt hoa thường nên thường không tìm được bao nhiêu. Muốn bỏ qua phân biệt hoa thường thì theo sau từ khoá bằng một escape character: nhập từ cần tìm, rồi một dấu gạch chéo theo chiều ngược lại, rồi chữ c. Nghĩ chữ c là case để nhớ.
/this\c
Nhảy tới một dòng cụ thể bằng cách gõ dấu hai chấm rồi số dòng:
:3
Copy cả một dòng text khi đang ở command mode: nhấn phím y hai lần. Nghĩ y là yank để nhớ. Dán dòng vừa copy bằng cách nhấn p, vẫn trong command mode, và nhấn p bao nhiêu lần cũng được. Cắt một dòng text thì nhấn phím d hai lần, tức dd, sau đó dán lại chỗ khác cũng bằng p.
Nguồn tham khảo
Nguồn gốc: Khóa "Prep Course - Linux Foundation Certified System Administrator (LFCS) Certification" — phần "Essential Commands" (tìm file bằng find theo tên, thời gian, kích thước và permission; xem, biến đổi và so sánh text stream bằng cat, tac, head, tail, sed, cut, sort, uniq, diff và sdiff; demo pager less, more và trình soạn thảo vi), nền tảng KodeKloud. Giảng viên: Jeremy Morgan và Alexandru Andrei.