Mã thoát Linux giúp bạn viết các tập lệnh mạnh mẽ như thế nào

Tác giả Starlink, T.Tư 16, 2025, 11:23:36 CHIỀU

« Chủ đề trước - Chủ đề tiếp »

0 Thành viên và 1 Khách đang xem chủ đề.

Thoát khỏi sân khấu bên trái.

    Mỗi lệnh Linux trả về một mã thoát, trong đó 0 biểu thị thành công và giá trị khác không biểu thị thất bại.
    Bạn có thể sử dụng biến $? để truy cập mã thoát và kiểm soát luồng chương trình trong các tập lệnh.
    Hãy thận trọng với các mã thoát không thông thường thay đổi giữa các lệnh, như diff và curl.


Mọi lệnh Linux sẽ in ra thông báo lỗi nếu không thành công, nhưng thông tin này khó có thể tin cậy được. Các tập lệnh của bạn nên sử dụng mã thoát để mạnh mẽ nhất có thể.

1. Mã thoát là gì?

Khi bạn chạy lệnh Linux, lệnh có thể bị lỗi. Mọi thứ có thể xảy ra sai sót vì nhiều lý do, nhưng bạn thường sẽ thấy thông báo giải thích lý do:


Thông báo lỗi bạn thấy xuất hiện trên STDERR, một luồng khác với STDOUT, cho phép bạn tách biệt lỗi khỏi đầu ra mong đợi. Là người dùng, mô tả lỗi phải cung cấp thông tin, nhưng các tập lệnh và quy trình tự động khác cần biết về lỗi một cách đáng tin cậy hơn.

Bên cạnh các thông báo lỗi có thể đọc được, mỗi lệnh cũng trả về một mã thoát. Đây là một số từ 0 đến 255 cho biết lệnh đã thất bại hay thành công. Theo mặc định, mã thoát bị ẩn, nhưng shell của bạn cung cấp quyền truy cập vào giá trị này trong biến đặc biệt, $?


Lưu ý rằng giá trị 0 biểu thị thành công và giá trị khác không biểu thị thất bại. Bạn có thể kiểm tra giá trị này trên dòng lệnh, thẩm vấn nó trong một tập lệnh và sử dụng nó để kiểm soát luồng chương trình của bạn.

2. Tôi sử dụng mã thoát như thế nào?

Trong ví dụ trên, ls trả về mã thoát 0 khi thành công và 1 khi đối số của nó không phải là tệp hợp lệ. Điều này có vẻ trái ngược với trực giác và nó trái ngược với các ngôn ngữ lập trình thông thường coi 0 là giá trị sai và 1 là đúng. Tuy nhiên, thiết lập này cho phép một giá trị thành công chung duy nhất và nhiều giá trị để biểu diễn các loại lỗi khác nhau.

Cũng như việc lặp lại biến $? để kiểm tra thủ công trạng thái thoát, bạn có thể sử dụng nó với câu lệnh if có điều kiện. Nếu lệnh trả về trạng thái thoát 0, câu lệnh sẽ chạy khối then liên quan. Nếu không, nếu có, nó sẽ chạy khối else. Ví dụ:

Mã nguồn [Chọn]
if command; then echo "command succeeded"; else echo "command failed"; fi
Bạn có thể sử dụng cấu trúc này trong một tập lệnh shell, nơi dễ định dạng hơn để dễ đọc:

Mã nguồn [Chọn]
if command
    then echo "command succeeded"
    else echo "command failed"
fi

Điều này tương đương với:

Mã nguồn [Chọn]
command

if [ $? -eq 0 ]
    then echo "command succeeded"
    else echo "command failed"
fi

Phiên bản dài hơn sử dụng cú pháp ngoặc vuông để kiểm tra giá trị của $?, nhưng thường dễ hơn khi kiểm tra giá trị trực tiếp, như trong trường hợp đầu tiên. Hãy nhớ rằng $? luôn biểu thị mã thoát khỏi lệnh cuối cùng, vì vậy hãy cẩn thận. Bạn có thể bị cám dỗ xuất giá trị của $? để gỡ lỗi, ví dụ:

Mã nguồn [Chọn]
command

echo "command returned exit code:" $?

if [ $? -eq 0 ]
    then echo "command succeeded"
    else echo "command failed"
fi

Tuy nhiên, tại thời điểm kiểm tra điều kiện, $? sẽ có giá trị trạng thái thoát của echo, gần như chắc chắn là 0. Để tránh điều này, hãy kiểm tra lệnh trực tiếp trong câu lệnh có điều kiện hoặc lưu giá trị của $? vào một biến.

3. Còn điều gì cần biết về trạng thái thoát?

Mã thoát khá đơn giản, tuân theo nguyên tắc Unix và đây là một phần sức mạnh của chúng. Nhưng có một số lỗi và trường hợp ngoại lệ mà bạn nên biết.

3.1. Mã thoát khỏi Shell của bạn

Shell của bạn sẽ sử dụng mã thoát ngay cả khi lệnh bạn nhập không chạy đúng. Ví dụ, zsh và bash sử dụng 127 nếu lệnh không được tìm thấy và 126 nếu lệnh không thể thực thi:


3.2. Mã thoát không theo quy ước

Một số lệnh hơi bẻ cong các quy tắc của trạng thái thoát; dù sao thì chúng thực sự chỉ là các quy ước. Ví dụ, diff sử dụng giá trị 0 để chỉ "không có sự khác biệt", giá trị 1 để chỉ "sự khác biệt" và giá trị 2 để biểu thị lỗi:


Điều này có thể gây nhầm lẫn ý nghĩa của câu điều kiện; ví dụ:
   
Mã nguồn [Chọn]
if diff file1 file2; then
    echo these files are the same!
fi

Lệnh curl cũng hơi không trực quan. Bạn có thể mong đợi curl báo cáo lỗi HTTP, như lỗi 404 cho URL không tồn tại, dưới dạng mã trạng thái khác không, nhưng nó không làm vậy:


Bạn có thể sử dụng tùy chọn -f (--fail) để khiến curl hoạt động khác đi, trả về mã thoát là 22 khi lỗi:


Tuy nhiên, hãy lưu ý rằng điều này không được đảm bảo 100% và một số lỗi HTTP, bao gồm cả lỗi yêu cầu xác thực, vẫn sẽ trả về mã thoát là 0.

3.3. Sử dụng Exit Status trong một chuỗi lệnh

Một trong những lối tắt thoát trạng thái tốt nhất xảy ra mà bạn thậm chí không cần phải suy nghĩ về nó. Sau đây là một ví dụ đơn giản:
 
Mã nguồn [Chọn]
ls program &&./program && echo success || echo epic fail
Các toán tử logic && và || cho phép bạn chạy nhiều lệnh dựa trên trạng thái thoát của các lệnh khác. Chúng tương tự như các toán tử tương ứng trong hầu hết các ngôn ngữ lập trình, nhưng chúng tôn trọng quy ước mã thoát. Toán tử && sẽ chỉ chạy lệnh ở bên phải của nó nếu lệnh ở bên trái của nó trả về trạng thái thoát là 0. Toán tử || sẽ chỉ chạy lệnh ở bên phải của nó nếu lệnh ở bên trái của nó không thành công với trạng thái khác không.


Một trường hợp rất phổ biến xảy ra khi bạn xây dựng phần mềm từ đầu bằng công thức này:
   
Mã nguồn [Chọn]
./configure && make && make install
Ba phần của lệnh này thực hiện những điều sau:

   ./configure chạy một tập lệnh cục bộ để kiểm tra môi trường của bạn và tạo ra một Makefile.
    make chạy chương trình make và xây dựng phần mềm bằng cách sử dụng Makefile.
    make install sao chép tệp thực thi đã tạo vào một vị trí quen thuộc như /usr/local/bin.

Nếu bất kỳ phần nào của quá trình này bị lỗi, nó sẽ dừng ngay lập tức mà không cần thử phần còn lại.

3.4. Các lệnh đúng và sai

Hệ thống Linux của bạn bao gồm hai lệnh kỳ lạ: true và false. Mỗi lệnh này không làm gì cả, ngoại trừ việc trả về mã thoát thích hợp: lần lượt là 0 và 1:


Chúng có vẻ không hữu ích lắm, nhưng những lệnh này rất hữu ích cho việc thử nghiệm và một số tác vụ viết tập lệnh shell. Bạn có thể sử dụng true trong câu lệnh while để tạo vòng lặp vô hạn:

Mã nguồn [Chọn]
while true
do
    echo “running until you ^c”
    sleep 10
done

Bạn cũng có thể sử dụng chúng cùng với các toán tử logic để thay đổi hành vi lệnh. Ví dụ, bạn có thể tạm thời dừng một chuỗi lệnh dài khỏi việc chạy:

Mã nguồn [Chọn]
false && (none || of-these && commands || will-run)
Hoặc bạn có thể buộc chuỗi lệnh tiếp tục chạy, ngay cả khi một lệnh bị lỗi:
   
Mã nguồn [Chọn]
cat file-that-may-not-exist | true && echo done
3.5. Trả về mã thoát từ tập lệnh của riêng bạn

Có lẽ bạn đã từng sử dụng lệnh exit trước đây để thoát khỏi terminal. Về mặt kỹ thuật, lệnh này thoát khỏi shell của bạn và điều này có nghĩa là bạn cũng có thể sử dụng lệnh này để dừng một tập lệnh shell. Theo mặc định, các tập lệnh của bạn sẽ thoát với cùng mã như lệnh cuối cùng:


Nhưng bạn có thể thay đổi mã thoát bằng cách truyền tham số số vào lệnh thoát:
 
Mã nguồn [Chọn]
exit number
Bây giờ, tập lệnh của bạn sẽ thoát với mã trạng thái bạn đã cung cấp và bạn có thể sử dụng mã này để truyền đạt các điều kiện lỗi khác nhau từ chương trình của bạn đến các chương trình khác.

Khi bạn đã biết về chúng, lệnh thoát rất dễ sử dụng. Bạn thậm chí có thể sử dụng chúng mà không cần suy nghĩ nhiều, nhưng khi nói đến việc viết kịch bản, hãy đảm bảo rằng bạn đang sử dụng mã thoát một cách phù hợp.