Dưới đây là một ví dụ về một tập lệnh thực hiện try/catch/finallytrong bash.
Giống như các câu trả lời khác cho câu hỏi này, các ngoại lệ phải được bắt gặp sau khi thoát khỏi một quy trình con.
Các tập lệnh mẫu bắt đầu bằng cách tạo một fifo ẩn danh, được sử dụng để truyền các thông điệp chuỗi từ một command exceptionhoặc throwđến cuối trykhối gần nhất . Ở đây các tin nhắn được xóa khỏi fifo và được đặt trong một biến mảng. Trạng thái được trả về thông qua returnvà exitcác lệnh và được đặt trong một biến khác nhau. Để nhập một catchkhối, trạng thái này không được bằng không. Các yêu cầu khác để nhập một catchkhối được truyền dưới dạng tham số. Nếu kết thúc một catchkhối đạt được, thì trạng thái được đặt thành không. Nếu kết thúc finallykhối đạt được và trạng thái vẫn khác không, thì một cú ném ngầm chứa các thông điệp và trạng thái được thực thi. Kịch bản yêu cầu gọi hàm trycatchfinallychứa hàm xử lý ngoại lệ chưa được xử lý.
Cú pháp của trycatchfinallylệnh được đưa ra dưới đây.
trycatchfinally [-cde] [-h ERR_handler] [-k] [-o debug_file] [-u unhandled_handler] [-v variable] fifo function
Các -ctùy chọn thêm các cuộc gọi stack để các thông điệp ngoại lệ.
Các -dtùy chọn cho phép kết xuất debug.
Các -etùy chọn cho phép ngoại lệ lệnh.
Các -htùy chọn cho phép người dùng thay thế xử lý lệnh ngoại lệ riêng của họ.
Các -ktùy chọn thêm các cuộc gọi stack để kết xuất debug.
Các -otùy chọn thay thế các tập tin đầu ra mặc định là /dev/fd/2.
Các -utùy chọn cho phép người dùng thay thế xử lý ngoại lệ unhandled riêng của họ.
Các -vtùy chọn cho phép người dùng tùy chọn để vượt qua trở lại giá trị mặc dù việc sử dụng các lệnh Thay.
Tên fifotập tin fifo.
Hàm functionđược gọi trycatchfinallylà một quy trình con.
Lưu ý: Các cdkotùy chọn đã bị xóa để đơn giản hóa tập lệnh.
Cú pháp của catchlệnh được đưa ra dưới đây.
catch [[-enoprt] list ...] ...
Các tùy chọn được xác định dưới đây. Giá trị cho danh sách đầu tiên là trạng thái. Giá trị phụ là các thông điệp. Nếu có nhiều tin nhắn hơn danh sách, thì các tin nhắn còn lại sẽ bị bỏ qua.
-ecó nghĩa là [[ $value == "$string" ]](giá trị phải khớp với ít nhất một chuỗi trong danh sách)
-nnghĩa là [[ $value != "$string" ]](giá trị không thể khớp với bất kỳ chuỗi nào trong danh sách)
-ocó nghĩa là [[ $value != $pattern ]](giá trị không thể khớp với bất kỳ mẫu nào trong danh sách)
-pcó nghĩa là [[ $value == $pattern ]](giá trị có để khớp với ít nhất một mẫu trong danh sách)
-rcó nghĩa là [[ $value =~ $regex ]](giá trị phải khớp với ít nhất một biểu thức chính quy mở rộng trong danh sách)
-tcó nghĩa [[ ! $value =~ $regex ]](giá trị không thể khớp với bất kỳ biểu thức chính quy mở rộng nào trong danh sách)
Các try/catch/finallykịch bản được đưa ra dưới đây. Để đơn giản hóa tập lệnh cho câu trả lời này, hầu hết các kiểm tra lỗi đã được loại bỏ. Điều này làm giảm kích thước 64%. Một bản sao hoàn chỉnh của kịch bản này có thể được tìm thấy ở câu trả lời khác của tôi .
shopt -s expand_aliases
alias try='{ common.Try'
alias yrt='EchoExitStatus; common.yrT; }'
alias catch='{ while common.Catch'
alias hctac='common.hctaC; done; }'
alias finally='{ common.Finally'
alias yllanif='common.yllaniF; }'
DefaultErrHandler() {
echo "Orginal Status: $common_status"
echo "Exception Type: ERR"
}
exception() {
let "common_status = 10#$1"
shift
common_messages=()
for message in "$@"; do
common_messages+=("$message")
done
}
throw() {
local "message"
if [[ $# -gt 0 ]]; then
let "common_status = 10#$1"
shift
for message in "$@"; do
echo "$message" >"$common_fifo"
done
elif [[ ${#common_messages[@]} -gt 0 ]]; then
for message in "${common_messages[@]}"; do
echo "$message" >"$common_fifo"
done
fi
chmod "0400" "$common_fifo"
exit "$common_status"
}
common.ErrHandler() {
common_status=$?
trap ERR
if [[ -w "$common_fifo" ]]; then
if [[ $common_options != *e* ]]; then
common_status="0"
return
fi
eval "${common_errHandler:-} \"${BASH_LINENO[0]}\" \"${BASH_SOURCE[1]}\" \"${FUNCNAME[1]}\" >$common_fifo <$common_fifo"
chmod "0400" "$common_fifo"
fi
if [[ common_trySubshell -eq BASH_SUBSHELL ]]; then
return
else
exit "$common_status"
fi
}
common.Try() {
common_status="0"
common_subshell="$common_trySubshell"
common_trySubshell="$BASH_SUBSHELL"
common_messages=()
}
common.yrT() {
local "status=$?"
if [[ common_status -ne 0 ]]; then
local "message=" "eof=TRY_CATCH_FINALLY_END_OF_MESSAGES_$RANDOM"
chmod "0600" "$common_fifo"
echo "$eof" >"$common_fifo"
common_messages=()
while read "message"; do
[[ $message != *$eof ]] || break
common_messages+=("$message")
done <"$common_fifo"
fi
common_trySubshell="$common_subshell"
}
common.Catch() {
[[ common_status -ne 0 ]] || return "1"
local "parameter" "pattern" "value"
local "toggle=true" "compare=p" "options=$-"
local -i "i=-1" "status=0"
set -f
for parameter in "$@"; do
if "$toggle"; then
toggle="false"
if [[ $parameter =~ ^-[notepr]$ ]]; then
compare="${parameter#-}"
continue
fi
fi
toggle="true"
while "true"; do
eval local "patterns=($parameter)"
if [[ ${#patterns[@]} -gt 0 ]]; then
for pattern in "${patterns[@]}"; do
[[ i -lt ${#common_messages[@]} ]] || break
if [[ i -lt 0 ]]; then
value="$common_status"
else
value="${common_messages[i]}"
fi
case $compare in
[ne]) [[ ! $value == "$pattern" ]] || break 2;;
[op]) [[ ! $value == $pattern ]] || break 2;;
[tr]) [[ ! $value =~ $pattern ]] || break 2;;
esac
done
fi
if [[ $compare == [not] ]]; then
let "++i,1"
continue 2
else
status="1"
break 2
fi
done
if [[ $compare == [not] ]]; then
status="1"
break
else
let "++i,1"
fi
done
[[ $options == *f* ]] || set +f
return "$status"
}
common.hctaC() {
common_status="0"
}
common.Finally() {
:
}
common.yllaniF() {
[[ common_status -eq 0 ]] || throw
}
caught() {
[[ common_status -eq 0 ]] || return 1
}
EchoExitStatus() {
return "${1:-$?}"
}
EnableThrowOnError() {
[[ $common_options == *e* ]] || common_options+="e"
}
DisableThrowOnError() {
common_options="${common_options/e}"
}
GetStatus() {
echo "$common_status"
}
SetStatus() {
let "common_status = 10#$1"
}
GetMessage() {
echo "${common_messages[$1]}"
}
MessageCount() {
echo "${#common_messages[@]}"
}
CopyMessages() {
if [[ ${#common_messages} -gt 0 ]]; then
eval "$1=(\"\${common_messages[@]}\")"
else
eval "$1=()"
fi
}
common.GetOptions() {
local "opt"
let "OPTIND = 1"
let "OPTERR = 0"
while getopts ":cdeh:ko:u:v:" opt "$@"; do
case $opt in
e) [[ $common_options == *e* ]] || common_options+="e";;
h) common_errHandler="$OPTARG";;
u) common_unhandled="$OPTARG";;
v) common_command="$OPTARG";;
esac
done
shift "$((OPTIND - 1))"
common_fifo="$1"
shift
common_function="$1"
chmod "0600" "$common_fifo"
}
DefaultUnhandled() {
local -i "i"
echo "-------------------------------------------------"
echo "TryCatchFinally: Unhandeled exception occurred"
echo "Status: $(GetStatus)"
echo "Messages:"
for ((i=0; i<$(MessageCount); i++)); do
echo "$(GetMessage "$i")"
done
echo "-------------------------------------------------"
}
TryCatchFinally() {
local "common_errHandler=DefaultErrHandler"
local "common_unhandled=DefaultUnhandled"
local "common_options="
local "common_fifo="
local "common_function="
local "common_flags=$-"
local "common_trySubshell=-1"
local "common_subshell"
local "common_status=0"
local "common_command="
local "common_messages=()"
local "common_handler=$(trap -p ERR)"
[[ -n $common_handler ]] || common_handler="trap ERR"
common.GetOptions "$@"
shift "$((OPTIND + 1))"
[[ -z $common_command ]] || common_command+="=$"
common_command+='("$common_function" "$@")'
set -E
set +e
trap "common.ErrHandler" ERR
try
eval "$common_command"
yrt
catch; do
"$common_unhandled" >&2
hctac
[[ $common_flags == *E* ]] || set +E
[[ $common_flags != *e* ]] || set -e
[[ $common_flags != *f* || $- == *f* ]] || set -f
[[ $common_flags == *f* || $- != *f* ]] || set +f
eval "$common_handler"
}
Dưới đây là một ví dụ, giả sử tập lệnh trên được lưu trữ trong tệp có tên simple. Các makefifotập tin có chứa các kịch bản được mô tả trong câu trả lời này . Giả định được đưa ra là tệp có tên 4444kkkkkkhông tồn tại, do đó gây ra ngoại lệ xảy ra. Đầu ra thông báo lỗi từ ls 4444kkkkklệnh được tự động loại bỏ cho đến khi bên trong catchkhối thích hợp .
#!/bin/bash
#
if [[ $0 != ${BASH_SOURCE[0]} ]]; then
bash "${BASH_SOURCE[0]}" "$@"
return
fi
source simple
source makefifo
MyFunction3() {
echo "entered MyFunction3" >&4
echo "This is from MyFunction3"
ls 4444kkkkk
echo "leaving MyFunction3" >&4
}
MyFunction2() {
echo "entered MyFunction2" >&4
value="$(MyFunction3)"
echo "leaving MyFunction2" >&4
}
MyFunction1() {
echo "entered MyFunction1" >&4
local "flag=false"
try
(
echo "start of try" >&4
MyFunction2
echo "end of try" >&4
)
yrt
catch "[1-3]" "*" "Exception\ Type:\ ERR"; do
echo 'start of catch "[1-3]" "*" "Exception\ Type:\ ERR"'
local -i "i"
echo "-------------------------------------------------"
echo "Status: $(GetStatus)"
echo "Messages:"
for ((i=0; i<$(MessageCount); i++)); do
echo "$(GetMessage "$i")"
done
echo "-------------------------------------------------"
break
echo 'end of catch "[1-3]" "*" "Exception\ Type:\ ERR"'
hctac >&4
catch "1 3 5" "*" -n "Exception\ Type:\ ERR"; do
echo 'start of catch "1 3 5" "*" -n "Exception\ Type:\ ERR"'
echo "-------------------------------------------------"
echo "Status: $(GetStatus)"
[[ $(MessageCount) -le 1 ]] || echo "$(GetMessage "1")"
echo "-------------------------------------------------"
break
echo 'end of catch "1 3 5" "*" -n "Exception\ Type:\ ERR"'
hctac >&4
catch; do
echo 'start of catch' >&4
echo "failure"
flag="true"
echo 'end of catch' >&4
hctac
finally
echo "in finally"
yllanif >&4
"$flag" || echo "success"
echo "leaving MyFunction1" >&4
} 2>&6
ErrHandler() {
echo "EOF"
DefaultErrHandler "$@"
echo "Function: $3"
while read; do
[[ $REPLY != *EOF ]] || break
echo "$REPLY"
done
}
set -u
echo "starting" >&2
MakeFIFO "6"
TryCatchFinally -e -h ErrHandler -o /dev/fd/4 -v result /dev/fd/6 MyFunction1 4>&2
echo "result=$result"
exec >&6-
Kịch bản trên đã được thử nghiệm bằng cách sử dụng GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17). Đầu ra, từ việc chạy tập lệnh này, được hiển thị bên dưới.
starting
entered MyFunction1
start of try
entered MyFunction2
entered MyFunction3
start of catch "[1-3]" "*" "Exception\ Type:\ ERR"
-------------------------------------------------
Status: 1
Messages:
Orginal Status: 1
Exception Type: ERR
Function: MyFunction3
ls: 4444kkkkk: No such file or directory
-------------------------------------------------
start of catch
end of catch
in finally
leaving MyFunction1
result=failure
Một ví dụ khác sử dụng a throwcó thể được tạo bằng cách thay thế hàm MyFunction3bằng tập lệnh hiển thị bên dưới.
MyFunction3() {
echo "entered MyFunction3" >&4
echo "This is from MyFunction3"
throw "3" "Orginal Status: 3" "Exception Type: throw"
echo "leaving MyFunction3" >&4
}
Cú pháp của throwlệnh được đưa ra dưới đây. Nếu không có tham số nào, thì trạng thái và thông điệp được lưu trữ trong các biến được sử dụng thay thế.
throw [status] [message ...]
Đầu ra, từ việc thực thi tập lệnh đã sửa đổi, được hiển thị bên dưới.
starting
entered MyFunction1
start of try
entered MyFunction2
entered MyFunction3
start of catch "1 3 5" "*" -n "Exception\ Type:\ ERR"
-------------------------------------------------
Status: 3
Exception Type: throw
-------------------------------------------------
start of catch
end of catch
in finally
leaving MyFunction1
result=failure