Dưới đây là một ví dụ về một tập lệnh thực hiện try/catch/finally
trong 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 exception
hoặc throw
đến cuối try
khố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 return
và exit
các lệnh và được đặt trong một biến khác nhau. Để nhập một catch
khố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 catch
khối được truyền dưới dạng tham số. Nếu kết thúc một catch
khối đạt được, thì trạng thái được đặt thành không. Nếu kết thúc finally
khố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 trycatchfinally
chứa hàm xử lý ngoại lệ chưa được xử lý.
Cú pháp của trycatchfinally
lệ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 -c
tùy chọn thêm các cuộc gọi stack để các thông điệp ngoại lệ.
Các -d
tùy chọn cho phép kết xuất debug.
Các -e
tùy chọn cho phép ngoại lệ lệnh.
Các -h
tù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 -k
tùy chọn thêm các cuộc gọi stack để kết xuất debug.
Các -o
tùy chọn thay thế các tập tin đầu ra mặc định là /dev/fd/2
.
Các -u
tù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 -v
tù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 fifo
tập tin fifo.
Hàm function
được gọi trycatchfinally
là một quy trình con.
Lưu ý: Các cdko
tùy chọn đã bị xóa để đơn giản hóa tập lệnh.
Cú pháp của catch
lệ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.
-e
có nghĩa là [[ $value == "$string" ]]
(giá trị phải khớp với ít nhất một chuỗi trong danh sách)
-n
nghĩa là [[ $value != "$string" ]]
(giá trị không thể khớp với bất kỳ chuỗi nào trong danh sách)
-o
có 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)
-p
có nghĩa là [[ $value == $pattern ]]
(giá trị có để khớp với ít nhất một mẫu trong danh sách)
-r
có 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)
-t
có 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/finally
kị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 makefifo
tậ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 4444kkkkk
khô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 4444kkkkk
lệnh được tự động loại bỏ cho đến khi bên trong catch
khố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 throw
có thể được tạo bằng cách thay thế hàm MyFunction3
bằ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 throw
lệ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