Aşağıda try/catch/finally
bash içinde uygulanan bir komut dosyası örneği verilmiştir .
Bu sorunun diğer cevapları gibi, bir alt işlemden çıktıktan sonra istisnalar yakalanmalıdır.
Örnek komut dosyaları , en yakın bloğun bir satırından command exception
veya throw
sonuna kadar dize iletileri iletmek için kullanılan anonim bir fifo oluşturarak başlar try
. Burada mesajlar fifodan kaldırılır ve bir dizi değişkenine yerleştirilir. Durum döndürülür return
ve exit
komutlar verilir ve farklı bir değişkene yerleştirilir. Bir catch
blok girmek için bu durum sıfır olmamalıdır. Bir catch
blok girmek için diğer gereksinimler parametre olarak iletilir. Bir catch
bloğun sonuna ulaşılırsa, durum sıfıra ayarlanır. finally
Bloğun sonuna ulaşılırsa ve durum hala sıfırdan farklıysa, iletileri ve durumu içeren örtük bir atış yürütülür. Komut dosyası, trycatchfinally
işlenmeyen bir özel durum işleyici içeren işlevin çağrılmasını gerektirir .
trycatchfinally
Komutun sözdizimi aşağıda verilmiştir.
trycatchfinally [-cde] [-h ERR_handler] [-k] [-o debug_file] [-u unhandled_handler] [-v variable] fifo function
Bu -c
seçenek, çağrı yığınını istisna mesajlarına ekler.
Bu -d
seçenek hata ayıklama çıktısını etkinleştirir.
Bu -e
seçenek komut istisnalarını etkinleştirir.
Bu -h
seçenek kullanıcının kendi komut istisna işleyicisini değiştirmesine izin verir.
Bu -k
seçenek, çağrı yığınını hata ayıklama çıktısına ekler. Seçeneğidir varsayılan çıkış dosyasının yerini alır .
Bu seçenek kullanıcının kendi işlenmeyen özel durum işleyicisini değiştirmesine olanak tanır.
Bu seçenek, kullanıcıya Komut Değiştirme işlevini kullanırken değerleri geri gönderme seçeneği sunar. Fifo dosya adıdır.
Bu işlev bir alt işlem olarak adlandırılır .
-o
/dev/fd/2
-u
-v
fifo
function
trycatchfinally
Not: cdko
Komut dosyasını basitleştirmek için seçenekler kaldırılmıştır.
catch
Komutun sözdizimi aşağıda verilmiştir.
catch [[-enoprt] list ...] ...
Seçenekler aşağıda tanımlanmıştır. İlk listenin değeri durumdur. Sonraki değerler mesajlardır. Listelerden daha fazla mesaj varsa, kalan mesajlar yok sayılır.
-e
araçlar [[ $value == "$string" ]]
(değer listesindeki en az bir dize maç için vardır)
-n
araçlar [[ $value != "$string" ]]
(değer listesindeki dizeleri herhangi maç olamaz)
-o
araçlar[[ $value != $pattern ]]
(değer listesindeki kalıpların hiçbiriyle aynı olamaz)
-p
araçlar [[ $value == $pattern ]]
(değer vardır listedeki en az bir deseni eşleştirmek için)
-r
anlamına gelir [[ $value =~ $regex ]]
(değer listedeki en az bir genişletilmiş normal ifadeyle eşleşmelidir)
-t
anlamına gelir [[ ! $value =~ $regex ]]
(değer listedeki genişletilmiş normal ifadelerin hiçbiriyle eşleşemez)
try/catch/finally
Senaryo aşağıda verilmiştir. Bu yanıt için komut dosyasını basitleştirmek üzere hata denetiminin çoğu kaldırıldı. Bu, boyutu% 64 oranında azalttı. Bu betiğin tam bir kopyası diğer cevabımda bulunabilir .
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"
}
Aşağıda, yukarıdaki komut dosyasının adlı dosyada depolandığını varsayan bir örnek bulunmaktadır simple
. makefifo
Dosya açıklanan komut dosyasını içeren bu cevap . Adındaki dosyanın 4444kkkkk
mevcut olmadığı varsayılır ve bu nedenle bir istisnanın oluşmasına neden olur. Komuttan çıkan hata mesajı ls 4444kkkkk
, uygun catch
bloğun içine girene kadar otomatik olarak bastırılır .
#!/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-
Yukarıdaki komut dosyası kullanılarak test edilmiştir GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
. Bu komut dosyasını çalıştırmanın çıktısı aşağıda gösterilmiştir.
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
A kullanan başka bir örnek throw
, işlev MyFunction3
aşağıda gösterilen komut dosyasıyla değiştirilerek oluşturulabilir .
MyFunction3() {
echo "entered MyFunction3" >&4
echo "This is from MyFunction3"
throw "3" "Orginal Status: 3" "Exception Type: throw"
echo "leaving MyFunction3" >&4
}
throw
Komutun sözdizimi aşağıda verilmiştir. Herhangi bir parametre yoksa, bunun yerine değişkenlerde saklanan durum ve mesajlar kullanılır.
throw [status] [message ...]
Değiştirilmiş komut dosyasının yürütülmesinden elde edilen çıktı aşağıda gösterilmiştir.
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