Thực hiện exit
trong một subshell là một cạm bẫy:
#!/bin/bash
function calc { echo 42; exit 1; }
echo $(calc)
Kịch bản in 42, thoát khỏi lớp con với mã trả về 1
và tiếp tục với tập lệnh. Ngay cả việc thay thế cuộc gọi bằng echo $(CALC) || exit 1
cũng không giúp ích gì vì mã trả về echo
là 0 bất kể mã trả về là gì calc
. Và calc
được thực hiện trước echo
.
Khó hiểu hơn nữa là cản trở hiệu ứng của exit
nó bằng cách gói nó vào phần local
dựng sẵn như trong đoạn script sau. Tôi đã vấp phải vấn đề khi tôi viết một hàm để xác minh giá trị đầu vào. Thí dụ:
Tôi muốn tạo một tệp có tên "năm tháng day.log", tức là 20141211.log
cho ngày hôm nay. Ngày được nhập bởi người dùng có thể không cung cấp giá trị hợp lý. Do đó, trong chức năng của mình, fname
tôi kiểm tra giá trị trả về của date
để xác minh tính hợp lệ của đầu vào của người dùng:
#!/bin/bash
doit ()
{
local FNAME=$(fname "$1") || exit 1
touch "${FNAME}"
}
fname ()
{
date +"%Y%m%d.log" -d"$1" 2>/dev/null
if [ "$?" != 0 ] ; then
echo "fname reports \"Illegal Date\"" >&2
exit 1
fi
}
doit "$1"
Có vẻ tốt. Hãy để kịch bản được đặt tên s.sh
. Nếu người dùng gọi tập lệnh với ./s.sh "Thu Dec 11 20:45:49 CET 2014"
, tập tin 20141211.log
được tạo. Tuy nhiên, nếu người dùng gõ ./s.sh "Thu hec 11 20:45:49 CET 2014"
, thì tập lệnh xuất ra:
fname reports "Illegal Date"
touch: cannot touch ‘’: No such file or directory
Dòng fname…
nói rằng dữ liệu đầu vào xấu đã được phát hiện trong lớp con. Nhưng exit 1
ở cuối local …
dòng không bao giờ được kích hoạt bởi vì lệnh local
luôn luôn quay trở lại 0
. Điều này là do local
được thực thi sau $(fname)
và do đó ghi đè mã trả về của nó. Và vì điều đó, kịch bản tiếp tục và gọi touch
với một tham số trống. Ví dụ này đơn giản nhưng hành vi của bash có thể khá khó hiểu trong một ứng dụng thực tế. Tôi biết, các lập trình viên thực sự không sử dụng người địa phương.☺
Để làm rõ: Không có local
, tập lệnh sẽ hủy bỏ như mong đợi khi nhập ngày không hợp lệ.
Cách khắc phục là chia dòng như
local FNAME
FNAME=$(fname "$1") || exit 1
Hành vi lạ tuân theo tài liệu local
trong trang man của bash: "Trạng thái trả về là 0 trừ khi địa phương được sử dụng bên ngoài hàm, tên không hợp lệ được cung cấp hoặc tên là biến chỉ đọc."
Mặc dù không phải là một lỗi nhưng tôi cảm thấy rằng hành vi của bash là phản trực giác. Tuy nhiên, tôi nhận thức được trình tự thực hiện, local
không nên che dấu một nhiệm vụ bị hỏng.
Câu trả lời ban đầu của tôi có một số điểm không chính xác. Sau một cuộc thảo luận sâu sắc và sâu sắc với mikeerv (cảm ơn bạn vì điều đó) tôi đã sửa chữa chúng.