Khi bạn gõ một lệnh bất kỳ vào terminal Linux, có bao giờ bạn tự hỏi điều gì thực sự đang diễn ra bên trong hệ thống không? Hành động tưởng chừng đơn giản này lại ẩn chứa một quy trình phức tạp, tùy thuộc vào việc lệnh đó là một chương trình thực thi, một shell script, một lệnh nội bộ của shell (builtin), một hàm do người dùng định nghĩa, hay một bí danh (alias). Việc hiểu rõ cơ chế này không chỉ giúp bạn sử dụng Linux hiệu quả hơn mà còn cung cấp cái nhìn sâu sắc về kiến trúc của hệ điều hành.
Bài viết này của thuthuatpc.net sẽ đi sâu vào từng loại lệnh phổ biến trong Linux, giải thích cách shell (đặc biệt là Bash) xác định và thực thi chúng. Chúng ta sẽ cùng khám phá thứ tự ưu tiên mà shell sử dụng, cũng như những đặc điểm riêng biệt của mỗi loại lệnh.
Các Loại Lệnh Linux Phổ Biến và Thứ Tự Ưu Tiên
Trước khi đi vào chi tiết, hãy cùng tìm hiểu các dạng lệnh khác nhau mà bạn có thể gặp trong môi trường Linux:
- Chương trình (nhị phân, hoặc thực thi – Program/Binary): Đây là một tệp trên đĩa, được lưu trữ dưới định dạng nhận dạng được (ví dụ: ELF trên Linux, Mach-O trên Mac). Định dạng này là cấp thấp, thân thiện với máy, cho phép shell chuyển giao cho nhân hệ điều hành (kernel) để thực thi.
- Shell Script: Một dạng chương trình cấp cao hơn nhiều, được viết bằng văn bản mà con người có thể đọc được. Một shell script có thể gọi các lệnh khác và thường được viết bằng ngôn ngữ thông dịch như Perl hoặc Python.
- Lệnh nội bộ (Builtin): Là các lệnh được tích hợp sẵn trong shell. Chúng luôn có sẵn và thường hoạt động ở cấp độ thấp để thực hiện các thao tác ảnh hưởng trực tiếp đến chính shell.
- Hàm Shell (Shell Function): Giống như một shell script thu nhỏ, đóng gói một tập hợp các lệnh dưới một tên duy nhất. Các hàm shell thường được tải để luôn có sẵn khi cần gọi.
- Bí danh (Alias): Là một tên thay thế cho bất kỳ loại lệnh nào ở trên. Bạn có thể tạo một alias ngắn gọn cho một lệnh dài hơn để tiết kiệm thời gian và công sức khi thực hiện các tác vụ phổ biến.
Thứ tự mà một shell như Bash tìm kiếm và ưu tiên các loại lệnh này là rất quan trọng. Nói một cách đơn giản, Bash sẽ cố gắng thực thi các lệnh theo thứ tự sau: alias, function, builtin, program/script.
1. Chạy Chương Trình Thực Thi (Executable Program)
Loại lệnh quen thuộc nhất là tệp chương trình thực thi. Nhiều chương trình phổ biến là một phần của tiêu chuẩn POSIX, nghĩa là chúng có sẵn trên hầu hết các hệ thống Unix hoặc tương tự Unix, bao gồm Linux và macOS. Ví dụ về các lệnh như vậy bao gồm cat
, diff
, và vi
.
Các chương trình dòng lệnh khác—như Apache, Neofetch, hay tree—có thể cần được cài đặt vì chúng thường không được bao gồm trong một bản phân phối theo mặc định. Nhưng, giống như các chương trình lệnh POSIX, chúng sẽ chạy thông qua một tệp chương trình duy nhất nằm ở đâu đó trên đĩa của bạn.
Có hai cách phổ biến để chạy một chương trình. Đầu tiên, bạn có thể gõ đường dẫn đến tệp chương trình, ví dụ như ./myprog hoặc /usr/bin/awk. Nếu phần đầu tiên của lệnh của bạn chứa bất kỳ dấu gạch chéo nào, shell sẽ cố gắng tìm một tệp tại đường dẫn tương ứng. Ví dụ, bạn có thể chạy một chương trình tại /bin/cat
như sau:
/bin/cat myfile
Shell sau đó sẽ kiểm tra xem có chương trình thực thi nào tại /bin/cat
không, rồi chạy nó nếu tồn tại.
Trên Linux, các chương trình thực thi thường không có phần mở rộng tệp, vì vậy một lệnh như “grep” được chứa trong một tệp có tên grep. Đây chỉ là một quy ước, vì việc làm cho các tệp thực thi dễ nhận biết chỉ bằng tên tệp là tiện lợi. Tuy nhiên, nếu bạn muốn, bạn có thể tự do đặt tên các tệp thực thi của mình theo bất kỳ cách nào bạn muốn, ví dụ “myprog.exe” hoặc “myprog.RUNME”.
Trường hợp thứ hai xảy ra khi bạn gõ tên chương trình mà không có đường dẫn; ví dụ:
cat myfile
Với một hệ thống được thiết lập đúng cách, điều này sẽ có cùng hiệu ứng như trước, bởi vì shell sẽ định vị cùng một chương trình cat
. Nó làm như vậy bằng cách kiểm tra một biến môi trường đặc biệt có tên PATH, trông giống như thế này:
Ảnh minh họa biến môi trường PATH trong Linux hiển thị danh sách các thư mục tìm kiếm lệnh.
Đây là một danh sách các đường dẫn được phân tách bằng dấu hai chấm mà shell sẽ tìm kiếm để định vị một chương trình. Thứ tự là quan trọng vì bạn có thể có hai tệp cùng tên ở các vị trí khác nhau. Ví dụ, nếu bạn có một tệp /usr/bin/grep
và một tệp /bin/grep
, PATH ở trên sẽ khiến shell chạy phiên bản /usr/bin
. Hệ thống này cho phép bạn ghi đè một lệnh toàn hệ thống bằng một lệnh tùy chỉnh cụ thể hơn cho nhu cầu của bạn, điều này có thể rất hữu ích.
2. Thực Thi Shell Script
Một shell script chạy hơi khác so với một chương trình thực thi, và nó là một loại tệp rất khác. Một shell script đơn giản trông như thế này:
#!/bin/sh
echo Just a simple, pointless shell script
Dòng đầu tiên được gọi là “shebang” hoặc “hash-bang”, được đặt tên theo sự kết hợp của ký tự #
và !
. Đây là một chỉ dẫn đặc biệt cho shell biết nên sử dụng trình thông dịch (interpreter) nào cho phần còn lại của script. Trong trường hợp trên, đó là chính shell, tại /bin/sh
.
Phần còn lại của script là một chương trình văn bản mà trình thông dịch hiểu. Trong trường hợp này, shell có thể chạy các lệnh như echo
theo cách tương tự như khi bạn gõ chúng trên dòng lệnh.
Mặt khác, một shell script rất giống với một chương trình thực thi, và shell của bạn sẽ tìm kiếm nó bằng biến PATH theo cách tương tự. Đối với cả chương trình và shell script, điều quan trọng cần lưu ý là các quyền trên tệp tương ứng sẽ ảnh hưởng đến cách bạn chạy nó. Với quyền thực thi được đặt, bạn có thể chạy chương trình bằng cách gõ tên hoặc đường dẫn của nó. Nhưng nếu quyền thực thi không được đặt, bạn sẽ thấy một lỗi:
Lỗi khi cố gắng chạy tệp không có quyền thực thi trong terminal Linux.
Bạn luôn có thể chạy một tệp như vậy bằng cách chuyển tên của nó làm đối số cho một trình thông dịch thích hợp. Ví dụ, bạn có thể chạy một shell script như thế này:
sh myscript.sh
Bạn thậm chí có thể chạy một tệp thực thi như thế này, nhưng bạn sẽ cần phải tìm hiểu một chút trước. Lệnh file
cung cấp thông tin về loại tệp, bao gồm cả trình thông dịch của nó:
Lệnh file hiển thị thông tin chi tiết về file nhị phân thực thi và lệnh ls xác nhận thiếu quyền thực thi trên Linux.
Trong trường hợp này, bạn có thể chạy tệp nhị phân pwd
bằng cách sử dụng chương trình /lib/ld-linux-aarch64.so.1
:
Chạy một chương trình thực thi Linux thông qua trình thông dịch bất chấp thiếu quyền thực thi trực tiếp.
3. Lệnh Nội Bộ Shell (Builtin)
Một số lệnh có tính chất cơ bản đến mức chúng cần hoạt động theo một cách hơi khác. Đây được gọi là các lệnh nội bộ (builtins) vì chúng, trên thực tế, là một phần của chính shell.
Builtins thường là các lệnh cấp thấp và bạn sử dụng thường xuyên: cd
, echo
, và kill
là những ví dụ phổ biến. Không giống như các chương trình và shell script, các lệnh này không tương ứng với một tệp cụ thể vì chúng là một phần của chính shell, được biên dịch vào tệp thực thi của shell.
Một số hệ thống có thể bao gồm các script wrapper chạy builtins. Ví dụ, trên macOS, có một script /usr/bin/cd
sử dụng lệnh builtin
để chạy builtin tương ứng. Điều này là để tương thích với POSIX, và bạn có thể bỏ qua phiên bản script này.
Các lệnh builtin có thể khó tìm hiểu vì chúng hoặc không có trang hướng dẫn (manual entry) riêng (ví dụ: trên Ubuntu Linux) hoặc tất cả chúng đều có một trang hướng dẫn lớn, chung (macOS). Chương trình tldr là một cách tuyệt vời để tìm hiểu thêm về các lệnh builtin.
Vì những lý do tương tự, các lệnh builtin thường hỗ trợ ít tùy chọn dòng lệnh hơn so với các chương trình đầy đủ. Ví dụ, cd
thường chỉ hỗ trợ hai tùy chọn khá ít được biết đến: -L
(để theo dõi các symbolic link) và -P
(để tránh các symbolic link). Điều này có nghĩa là, đáng tiếc, các tùy chọn như -h
hoặc --help
thường không được hỗ trợ.
4. Gọi Hàm Shell (Shell Function)
Một lệnh cũng có thể là một lời gọi đến một hàm. Bất cứ lúc nào trong một phiên làm việc, bạn có thể tải hoặc định nghĩa một hàm bằng cú pháp này (trong bash):
function_name() {
commands
}
Ngoài ra, bạn có thể sử dụng cú pháp chèn từ khóa “function” trước tên hàm và bỏ dấu ngoặc đơn sau nó:
function function_name {
commands
}
Sau đó, bạn có thể gọi một hàm bằng cách gõ tên của nó, theo sau là bất kỳ đối số nào. Ví dụ, tôi có hàm trợ giúp sau được định nghĩa trong một tệp:
mkd () { mkdir -p "$@" && cd "$@" || exit; }
Sau đó, tôi tải tệp này từ script điều khiển khởi động của shell (~/.zshrc
), và tôi có thể chạy lệnh này để tạo một thư mục mới và ngay lập tức chuyển đến đó:
mkd new_dir
Bạn có thể gọi một hàm như vậy trên dòng lệnh bất cứ khi nào bạn muốn. Bạn cũng có thể gọi nó từ các script, nhưng hãy nhớ rằng nó cần được tải (sử dụng lệnh source
nội bộ, hoặc tương đương là .
, để làm điều đó).
5. Sử Dụng Bí Danh Lệnh (Alias)
Alias là một tiện ích giúp bạn tiết kiệm thời gian khi gõ các tên lệnh dài hoặc các tùy chọn phổ biến. Ví dụ, trên hệ thống của tôi, tôi có một alias để đảm bảo danh sách tệp được hiển thị có màu, với hậu tố loại:
alias ls='ls -GF'
Bây giờ, nếu tôi chỉ gõ “ls,” lệnh “ls -GF” sẽ chạy thay thế.
Bạn có thể tạo một alias bất cứ lúc nào, bằng cách sử dụng lệnh alias
nội bộ, ví dụ:
alias ls="ls -GF"
Lưu ý rằng dấu ngoặc kép là bắt buộc khi bạn chạy alias
, nếu chuỗi thay thế chứa bất kỳ khoảng trắng nào. Bạn có thể xem tất cả các alias bằng cách chạy alias
, và bạn có thể xóa một alias bằng lệnh unalias
. Một lần nữa, bạn sẽ muốn thêm các alias vào tệp khởi động, như ~/.zshrc
hoặc ~/.bashrc
, nếu bạn muốn chúng áp dụng trên các phiên làm việc.
Kết Luận
Việc hiểu rõ cách hoạt động của các lệnh Linux không chỉ là kiến thức nền tảng mà còn là chìa khóa để bạn trở thành một người dùng hoặc quản trị viên hệ thống thành thạo. Từ các chương trình nhị phân nhỏ gọn đến các shell script mạnh mẽ, hay các lệnh builtin tiện lợi, hàm shell tùy chỉnh và bí danh tiết kiệm thời gian, mỗi loại lệnh đều có vai trò và cơ chế riêng biệt. Nắm vững thứ tự ưu tiên mà shell tìm kiếm và thực thi các lệnh này sẽ giúp bạn khắc phục sự cố, tối ưu hóa quy trình làm việc và tận dụng tối đa sức mạnh của hệ điều hành Linux.
Bạn còn thắc mắc nào về cách lệnh Linux hoạt động không? Hoặc bạn có mẹo hay alias yêu thích muốn chia sẻ? Hãy chia sẻ ý kiến của bạn bên dưới trong phần bình luận!