15+ mẹo và thủ thuật Bash giúp bạn làm chủ dòng lệnh Linux (Phần 1)

15+ mẹo và thủ thuật Bash giúp bạn làm chủ dòng lệnh Linux (Phần 1)

Bash – công cụ dòng lệnh mạnh mẽ trong hệ sinh thái Linux – mang đến khả năng xử lý linh hoạt và hiệu quả gần như không giới hạn. Từ thao tác với hàng loạt tệp tin, chỉnh sửa văn bản, xử lý dữ liệu lớn (Big Data), tối ưu quản trị hệ thống cho đến tự động hóa quy trình DevOps, Bash luôn là lựa chọn đáng tin cậy cho các kỹ sư phần mềm, sysadmin và lập trình viên.

Trong loạt bài chuyên sâu này – mà bạn đang đọc phần mở đầu – chúng tôi sẽ chia sẻ những mẹo, thủ thuật và phương pháp thực chiến giúp bạn nâng cao kỹ năng sử dụng Bash một cách chuyên nghiệp. Kể cả khi bạn đã quen thuộc với dòng lệnh, bạn vẫn sẽ tìm thấy những kiến thức mới mẻ, ứng dụng thực tiễn và có giá trị trong công việc hằng ngày.

Hãy bắt đầu hành trình làm chủ Bash – một kỹ năng không thể thiếu trong thế giới IT hiện đại!

DataOnline sẽ hướng dẫn bạn các cách như sau:

● Các mẹo, thủ thuật và phương pháp hữu ích của dòng lệnh Bash
● Cách tương tác với dòng lệnh Bash một cách nâng cao
● Cách rèn luyện kỹ năng Bash tổng thể và trở thành một người dùng Bash chuyên nghiệp hơn

Phần mềm yêu cầu và các quy ước sử dụng

Danh mục Yêu cầu, quy ước hoặc phiên bản phần mềm sử dụng
Hệ thống Linux (không phụ thuộc vào bản phân phối)
Phần mềm Dòng lệnh Bash, hệ thống dựa trên Linux
Khác Các tiện ích khác nhau được tích hợp sẵn trong shell Bash theo mặc định, hoặc có thể được cài đặt bằng lệnh sudo apt-get install tool-name (trong đó tool-name đại diện cho công cụ bạn muốn cài đặt)

Các quy ước

  • Dấu # – yêu cầu các lệnh Linux được thực thi với quyền root, trực tiếp với tư cách là người dùng root hoặc bằng cách sử dụng lệnh sudo
  • Dấu $ – yêu cầu các lệnh Linux được thực hiện với tư cách người dùng thường không có đặc quyền

Ví dụ 1: Xem các tiến trình đang truy cập một file nhất định

Bạn có muốn biết tiến trình nào đang truy cập vào một file cụ thể không? Điều này rất dễ dàng thực hiện với lệnh tích hợp sẵn của Bash, fuser:

$ fuser -a /usr/bin/gnome-calculator
/usr/bin/gnome-calculator: 619672e
$ ps -ef | grep 619672 | grep -v grep
abc       619672    3136  0 13:13 ?        00:00:01 gnome-calculator

Như bạn thấy, file /usr/bin/gnome-calculator (một file binary) hiện đang được sử dụng bởi tiến trình có ID 619672. Kiểm tra ID tiến trình đó bằng lệnh ps, chúng ta nhanh chóng nhận ra rằng người dùng abc đã khởi chạy máy tính tính và khởi chạy lúc 13:13.

Chữ “e” sau PID (Process ID) biểu thị rằng đây là một chương trình thực thi đang được chạy. Có rất nhiều các chỉ dấu khác tương tự, bạn có thể dùng man fuser để xem chi tiết. Công cụ fuser rất mạnh mẽ, đặc biệt khi kết hợp với lệnh lsof (liệt kê các file đang mở):

Giả sử chúng ta đang gỡ lỗi một máy tính từ xa cho một người dùng sử dụng môi trường desktop Ubuntu tiêu chuẩn. Người dùng khởi chạy máy tính tính, và bây giờ toàn bộ màn hình của họ bị treo. Chúng ta muốn từ xa kết thúc tất cả các tiến trình liên quan đến màn hình bị khóa, mà không cần khởi động lại máy chủ, theo thứ tự ưu tiên của mức độ quan trọng của các tiến trình đó.

# lsof | grep calculator | grep "share" | head -n1
xdg-deskt    3111                                 abc  mem       REG              253,1          3009   12327296 /usr/share/locale-langpack/en_AU/LC_MESSAGES/gnome-calculator.mo
# fuser -a /usr/share/locale-langpack/en_AU/LC_MESSAGES/gnome-calculator.mo
/usr/share/locale-langpack/en_AU/LC_MESSAGES/gnome-calculator.mo:  3111m  3136m 619672m 1577230m
# ps -ef | grep -E "3111|3136|619672|1577230" | grep -v grep
abc         3111    2779  0 Aug03 ?        00:00:11 /usr/libexec/xdg-desktop-portal-gtk
abc         3136    2779  5 Aug03 ?        03:08:03 /usr/bin/gnome-shell
abc       619672    3136  0 13:13 ?        00:00:01 gnome-calculator
abc      1577230    2779  0 Aug04 ?        00:03:15 /usr/bin/nautilus --gapplication-service

Đầu tiên, chúng ta đã liệt kê tất cả các file đang mở được sử dụng bởi máy tính tính bằng lệnh lsof. Để giữ cho kết quả ngắn gọn, chúng ta chỉ liệt kê kết quả hàng đầu của một file chia sẻ duy nhất. Sau đó, chúng ta dùng fuser để xác định tiến trình nào đang sử dụng file đó, từ đó lấy được các PID. Cuối cùng, chúng ta dùng grep với toán tử OR (|) để tìm ra tên tiến trình thực tế. Qua đó, ta thấy trong khi máy tính tính được khởi chạy lúc 13:13, các tiến trình khác đã chạy lâu hơn.

Tiếp theo, chúng ta có thể gửi lệnh, ví dụ như kill -9 619672 để kiểm tra xem vấn đề có được giải quyết hay không. Nếu không, chúng ta có thể thử dừng tiến trình 1577230 (quản lý file Nautilus chia sẻ), tiến trình 3136 (shell tổng thể), hoặc cuối cùng là tiến trình 3111, dù rằng tiến trình này có thể kết thúc một phần lớn trải nghiệm desktop của người dùng và khó khởi động lại.

Ví dụ 2: Gỡ lỗi các script của bạn

Giả sử bạn đã viết một script xuất sắc với nhiều đoạn code phức tạp, chạy nó và thấy xuất hiện lỗi trong đầu ra, mà ban đầu thì có vẻ không hợp lý. Ngay cả sau khi gỡ lỗi một lúc, bạn vẫn chưa tìm ra nguyên nhân khi script đang chạy.

Giải pháp đến từ lệnh bash -xbash -x cho phép bạn chạy script test.sh và xem chính xác mọi diễn biến xảy ra:

#!/bin/bash
VAR1="Hello linuxconfig.org readers!"
VAR2="------------------------------"
echo ${VAR1}
echo ${VAR2}

Thực thi:

$ bash -x ./test.sh
+ VAR1='Hello linuxconfig.org readers!'
+ VAR2=------------------------------
+ echo Hello linuxconfig.org 'readers!'
Hello linuxconfig.org readers!
+ echo ------------------------------
-------------------------

Như bạn thấy, lệnh bash -x đã cho chúng ta thấy chính xác từng bước đã xảy ra. Bạn cũng có thể dễ dàng chuyển hướng đầu ra của lệnh này vào một file bằng cách thêm 2>&1 | tee my_output.log vào cuối lệnh:

$ bash -x ./test.sh 2>&1 | tee my_output.log
... same output ...
$ cat my_output.log
+ VAR1='Hello linuxconfig.org readers!'
+ VAR2=------------------------------
+ echo Hello linuxconfig.org 'readers!'
Hello linuxconfig.org readers!
+ echo ------------------------------
------------------------------

Lệnh 2>&1 sẽ chuyển hướng đầu ra lỗi (stderr: thông báo lỗi xảy ra trong quá trình thực thi) sang đầu ra chuẩn (stdout: đầu ra bạn thường thấy trên terminal) và ghi lại toàn bộ đầu ra từ bash -x. Lệnh tee sẽ ghi lại toàn bộ đầu ra từ stdout và viết nó vào file đã chỉ định. Nếu bạn muốn ghi đè vào file (không tạo file mới trống), có thể dùng tee -a với tùy chọn -a để đảm bảo nội dung được nối thêm vào file hiện có.

Ví dụ 3: Một cạm bẫy phổ biến: sh -x không giống bash -x

Ví dụ trước đã cho thấy cách sử dụng bash -x, nhưng liệu bạn có thể dùng sh -x không? Nhiều người dùng mới của Bash có xu hướng chạy sh -x, nhưng đây là một sai lầm cho người mới; sh là một shell có giới hạn hơn rất nhiều. Mặc dù Bash dựa trên sh, nó có nhiều mở rộng hơn. Do đó, nếu bạn dùng sh -x để gỡ lỗi các script, bạn sẽ thấy xuất hiện lỗi lạ. Hãy xem ví dụ:

#!/bin/bash

TEST="abc"
if [[ "${TEST}" == *"b"* ]]; then
  echo "yes, in there!"
fi

Thực thi:

$ ./test.sh
yes, in there!
$ bash -x ./test.sh
+ TEST=abc
+ [[ abc == *\b* ]]
+ echo 'yes, in there!'
yes, in there!
$ sh -x ./test.sh
+ TEST=abc
+ [[ abc == *b* ]]
./test: 4: [[: not found

Tại đây, bạn có thể thấy một script nhỏ test.sh được thực thi để kiểm tra xem ký tự (b) có xuất hiện trong chuỗi đầu vào được định nghĩa bởi biến TEST hay không. Script chạy tốt và với bash -x chúng ta có thể thấy quá trình thực thi diễn ra đúng như mong đợi, bao gồm cả đầu ra.

Tuy nhiên, khi sử dụng sh -x, mọi thứ trở nên sai lệch; shell sh không thể hiểu được cú pháp [[ và do đó báo lỗi, cả khi hiển thị đầu ra với sh -x lẫn khi thực thi script. Nguyên nhân là vì cú pháp điều kiện nâng cao được triển khai trong Bash không tồn tại trong sh.

Ví dụ 4: uniq hay không uniq – đó là vấn đề!

Bạn đã bao giờ muốn sắp xếp một file và chỉ liệt kê các mục duy nhất chưa? Ban đầu, việc này có vẻ đơn giản bằng cách dùng lệnh uniq có sẵn của Bash:

$ cat input.txt 
1
2
2
3
3
3
$ cat input.txt 
1
2
2
3
3
3

Tuy nhiên, nếu chúng ta sửa đổi file input một chút, sẽ gặp phải vấn đề về tính duy nhất:

$ cat input.txt 
3
1
2
3
2
3
3
3
$ cat input.txt | uniq
3
1
2
3
2
3

Nguyên nhân là vì uniq theo mặc định sẽ lọc các dòng trùng nhau liền kề, với các dòng trùng nhau sẽ được gộp thành lần xuất hiện đầu tiên như hướng dẫn trong tài liệu của uniq. Hay nói cách khác, chỉ những dòng nào hoàn toàn giống với dòng phía trước mới bị loại bỏ.

Trong ví dụ, điều này thể hiện qua việc ba dòng “3” cuối cùng được gộp thành một dòng “3” duy nhất. Phương pháp này có thể chỉ phù hợp với những trường hợp, ứng dụng cụ thể với số lượng nhất định.

Tuy nhiên, chúng ta có thể điều chỉnh uniq một chút để chỉ lấy ra những dòng thực sự là duy nhất bằng cách sử dụng tham số -u:

$ cat input.txt  # Note that the '#' symbols were added after execution, to clarify something (read below)
3  #
1  #
2  #
3  #
2  #
3
3
3
$ cat input.txt | uniq -u 
3
1
2
3
2

Thấy có vẻ hơi rối, đúng không? Hãy nhìn kỹ vào file input và đầu ra, bạn sẽ thấy chỉ những dòng xuất hiện một cách riêng lẻ (được đánh dấu bởi ‘#’ trong ví dụ trên sau khi thực thi) mới được xuất ra.

Ba dòng “3” cuối cùng không được xuất ra vì chúng không phải là duy nhất. Phương pháp lọc theo tính duy nhất này lại chỉ có hiệu quả trong một số trường hợp hạn chế trong thực tế.

Chúng ta có thể có được một giải pháp thích hợp hơn cho bài toán duy nhất bằng cách sử dụng một công cụ tích hợp khác của Bash; đó là sort:

$ cat input.txt 
1
2
2
3
3
3
$ cat input.txt | sort -u
1
2
3

DID YOU KNOW?

Bạn có thể bỏ qua lệnh cat trong các ví dụ trên và chỉ định file cho uniq hoặc sort đọc trực tiếp? Ví dụ: sort -u input.txt

Tuyệt vời! Phương pháp này hữu dụng trong nhiều script khi chúng ta cần một danh sách thực sự gồm các mục duy nhất. Ưu điểm thêm nữa là danh sách được sắp xếp gọn gàng cùng lúc (dù chúng ta có thể muốn sử dụng thêm tùy chọn -n để sắp xếp theo thứ tự số dựa trên giá trị chuỗi số).

Kết luận

Việc lựa chọn Bash làm công cụ dòng lệnh chính trên hệ điều hành Linux không chỉ giúp bạn tối ưu hóa công việc, mà còn mở ra nhiều trải nghiệm thú vị trong quá trình sử dụng.

Trong bài viết này, bạn đã cùng chúng tôi khám phá những mẹo và thủ thuật Bash đơn giản nhưng cực kỳ hữu ích – những kỹ năng có thể áp dụng ngay vào công việc thực tế. Và đây mới chỉ là phần mở đầu trong chuỗi nội dung hướng dẫn chuyên sâu về Bash.

Hãy tiếp tục theo dõi các phần tiếp theo để nâng cấp kỹ năng dòng lệnh, làm chủ Bash Shell và khai thác tối đa sức mạnh của Linux một cách chuyên nghiệp hơn mỗi ngày.

Các ví dụ về mẹo và thủ thuật dòng lệnh Bash hữu ích – Phần 1

Các ví dụ về mẹo và thủ thuật dòng lệnh Bash hữu ích – Phần 2

Các ví dụ về mẹo và thủ thuật dòng lệnh Bash hữu ích – Phần 3

Các ví dụ về mẹo và thủ thuật dòng lệnh Bash hữu ích – Phần 4

Các ví dụ về mẹo và thủ thuật dòng lệnh Bash hữu ích – Phần 5

Để 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 *