Thực hiện exittrong 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ề 1và 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 1cũng không giúp ích gì vì mã trả về echolà 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 exitnó bằng cách gói nó vào phần localdự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.logcho 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, fnametô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 localluô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 touchvớ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 localtrong 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, localkhô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.