set -e
chấm dứt tập lệnh nếu gặp mã thoát khác, ngoại trừ trong một số điều kiện nhất định. Tóm lại những nguy hiểm của việc sử dụng nó trong một vài từ: nó không hành xử theo cách mọi người nghĩ.
Theo tôi, nó nên được coi là một hack nguy hiểm tiếp tục tồn tại cho mục đích tương thích. Câu set -e
lệnh không biến shell từ một ngôn ngữ sử dụng mã lỗi thành ngôn ngữ sử dụng luồng điều khiển giống như ngoại lệ, nó chỉ cố gắng mô phỏng hành vi đó một cách kém.
Greg Wooledge có rất nhiều điều để nói về sự nguy hiểm của set -e
:
Trong liên kết thứ hai, có nhiều ví dụ khác nhau về hành vi không trực quan và không thể đoán trước của set -e
.
Một số ví dụ về hành vi không trực quan của set -e
(một số được lấy từ liên kết wiki ở trên):
đặt -e
x = 0
hãy x ++
tiếng vang "x là $ x"
Ở trên sẽ khiến tập lệnh shell thoát sớm, bởi vì let x++
trả về 0, được xử lý bởi let
từ khóa dưới dạng giá trị giả và biến thành mã thoát không khác. set -e
thông báo điều này, và âm thầm chấm dứt kịch bản.
đặt -e
[-d / opt / foo] && echo "Cảnh báo: foo đã được cài đặt. Sẽ ghi đè lên." > & 2
echo "Cài đặt foo ..."
Các công việc trên như mong đợi, in một cảnh báo nếu /opt/foo
đã tồn tại.
đặt -e
check_preingly_install () {
[-d / opt / foo] && echo "Cảnh báo: foo đã được cài đặt. Sẽ ghi đè lên." > & 2
}
check_preingly_install
echo "Cài đặt foo ..."
Ở trên, mặc dù sự khác biệt duy nhất là một dòng duy nhất đã được tái cấu trúc thành một hàm, sẽ chấm dứt nếu /opt/foo
không tồn tại. Điều này là do thực tế rằng nó hoạt động ban đầu là một ngoại lệ đặc biệt đối với set -e
hành vi của. Khi a && b
trả về giá trị khác, nó bị bỏ qua set -e
. Tuy nhiên, bây giờ là một hàm, mã thoát của hàm bằng với mã thoát của lệnh đó và hàm trả về giá trị khác sẽ âm thầm chấm dứt tập lệnh.
đặt -e
IFS = $ '\ n' đọc -d '' -r -a config_vars <config
Ở trên sẽ đọc các mảng config_vars
từ các tập tin config
. Như tác giả có thể dự định, nó chấm dứt với một lỗi nếu config
bị thiếu. Vì tác giả có thể không có ý định, nó âm thầm chấm dứt nếu config
không kết thúc trong một dòng mới. Nếu set -e
không được sử dụng ở đây, thì config_vars
nó sẽ chứa tất cả các dòng của tệp cho dù nó có kết thúc trong một dòng mới hay không.
Người dùng của Sublime Text (và các trình soạn thảo văn bản khác xử lý các dòng mới không chính xác), hãy cẩn thận.
đặt -e
nên_audit_user () {
nhóm nhóm cục bộ = "$ (nhóm" $ 1 ")"
cho nhóm trong nhóm $; làm
if ["$ group" = kiểm toán]; sau đó trả về 0; fi
làm xong
trả lại 1
}
if Should_audit_user "$ user"; sau đó
logger 'Blah'
fi
Tác giả ở đây có thể mong đợi một cách hợp lý rằng nếu vì lý do nào đó người dùng $user
không tồn tại, thì groups
lệnh sẽ thất bại và tập lệnh sẽ chấm dứt thay vì để người dùng thực hiện một số tác vụ không được kiểm tra. Tuy nhiên, trong trường hợp này, việc set -e
chấm dứt không bao giờ có hiệu lực. Nếu $user
không thể được tìm thấy vì một số lý do, thay vì chấm dứt tập lệnh, should_audit_user
hàm sẽ chỉ trả về dữ liệu không chính xác như thể set -e
không có hiệu lực.
Điều này áp dụng cho bất kỳ chức năng nào được gọi từ phần điều kiện của if
câu lệnh, bất kể được lồng sâu đến đâu, bất kể nó được định nghĩa ở đâu, bất kể bạn có chạy lại set -e
bên trong nó hay không. Việc sử dụng if
tại bất kỳ điểm nào sẽ vô hiệu hóa hoàn toàn ảnh hưởng của set -e
cho đến khi khối điều kiện được thực thi hoàn toàn. Nếu tác giả không nhận thức được sự cố này hoặc không biết toàn bộ ngăn xếp cuộc gọi của họ trong tất cả các tình huống có thể được gọi, thì họ sẽ viết mã lỗi và ý thức bảo mật sai được cung cấp set -e
ít nhất là một phần cho khiển trách.
Ngay cả khi tác giả nhận thức đầy đủ về cạm bẫy này, cách giải quyết là viết mã theo cách giống như người ta sẽ viết nó mà không cần set -e
, hiển thị hiệu quả công tắc đó ít hơn vô dụng; tác giả không chỉ phải viết mã xử lý lỗi thủ công như thể set -e
không có hiệu lực, mà sự hiện diện của set -e
có thể đã đánh lừa họ nghĩ rằng họ không phải làm vậy.
Một số nhược điểm nữa của set -e
:
- Nó khuyến khích mã cẩu thả. Trình xử lý lỗi hoàn toàn bị lãng quên, với hy vọng rằng bất cứ điều gì thất bại sẽ báo cáo lỗi theo một cách hợp lý. Tuy nhiên, với các ví dụ như
let x++
trên, đây không phải là trường hợp. Nếu kịch bản chết bất ngờ, nó thường âm thầm, gây cản trở việc gỡ lỗi. Nếu tập lệnh không chết và bạn mong đợi nó (xem điểm đạn trước đó), thì bạn có một lỗi tinh vi và quỷ quyệt hơn trên tay.
- Nó dẫn mọi người vào một cảm giác an toàn sai lầm. Xem lại
if
điểm đạn -condition.
- Những nơi mà vỏ kết thúc không nhất quán giữa các phiên bản vỏ hoặc vỏ. Có thể vô tình viết một tập lệnh hoạt động khác nhau trên một phiên bản bash cũ hơn do hành vi
set -e
bị điều chỉnh giữa các phiên bản đó.
set -e
là một vấn đề gây tranh cãi và một số người nhận thức được các vấn đề xung quanh nó đề xuất chống lại nó, trong khi một số người khác chỉ khuyên bạn nên cẩn thận trong khi nó đang hoạt động để biết những cạm bẫy. Có nhiều người mới viết kịch bản shell đề xuất set -e
trên tất cả các tập lệnh dưới dạng bắt tất cả các điều kiện lỗi, nhưng trong thực tế, nó không hoạt động theo cách đó.
set -e
không thay thế cho giáo dục.