我正在写一个shell脚本,需要检查终端应用程序是否已安装。我想使用TRY/CATCH命令来做到这一点,除非有更整洁的方法。
当前回答
你有陷阱http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html,这是不一样的,但你可以使用其他技术来达到这个目的
其他回答
根据我在这里找到的一些答案,我为自己的项目制作了一个小的帮助文件:
trycatch.sh
#!/bin/bash
function try()
{
[[ $- = *e* ]]; SAVED_OPT_E=$?
set +e
}
function throw()
{
exit $1
}
function catch()
{
export ex_code=$?
(( $SAVED_OPT_E )) && set +e
return $ex_code
}
function throwErrors()
{
set -e
}
function ignoreErrors()
{
set +e
}
下面是它在使用中的示例:
#!/bin/bash
export AnException=100
export AnotherException=101
# start with a try
try
( # open a subshell !!!
echo "do something"
[ someErrorCondition ] && throw $AnException
echo "do something more"
executeCommandThatMightFail || throw $AnotherException
throwErrors # automaticatly end the try block, if command-result is non-null
echo "now on to something completely different"
executeCommandThatMightFail
echo "it's a wonder we came so far"
executeCommandThatFailsForSure || true # ignore a single failing command
ignoreErrors # ignore failures of commands until further notice
executeCommand1ThatFailsForSure
local result = $(executeCommand2ThatFailsForSure)
[ result != "expected error" ] && throw $AnException # ok, if it's not an expected error, we want to bail out!
executeCommand3ThatFailsForSure
# make sure to clear $ex_code, otherwise catch * will run
# echo "finished" does the trick for this example
echo "finished"
)
# directly after closing the subshell you need to connect a group to the catch using ||
catch || {
# now you can handle
case $ex_code in
$AnException)
echo "AnException was thrown"
;;
$AnotherException)
echo "AnotherException was thrown"
;;
*)
echo "An unexpected exception was thrown"
throw $ex_code # you can rethrow the "exception" causing the script to exit if not caught
;;
esac
}
Bash中有TRY CATCH命令吗?
不。
Bash不像在许多编程语言中那样拥有那么多奢侈的东西。
bash中没有try/catch;但是,可以使用&&或||实现类似的行为。
使用| |:
如果command1失败,则command2按如下方式运行
command1 || command2
类似地,使用&&,如果command1成功,command2将运行
try/catch最接近的近似如下
{ # try
command1 &&
#save your output
} || { # catch
# save log for exception
}
此外,bash还包含一些错误处理机制
set -e
如果任何简单的命令失败,它将停止脚本。
还有为什么不是if…else。它是你最好的朋友。
以下是我在另一个答案中使用的简化脚本的完整副本。除了额外的错误检查之外,还有一个别名允许用户更改现有别名的名称。语法如下所示。如果省略new_alias参数,则别名将被删除。
ChangeAlias old_alias [new_alias]
完整的脚本如下所示。
common.GetAlias() {
local "oldname=${1:-0}"
if [[ $oldname =~ ^[0-9]+$ && oldname+1 -lt ${#FUNCNAME[@]} ]]; then
oldname="${FUNCNAME[oldname + 1]}"
fi
name="common_${oldname#common.}"
echo "${!name:-$oldname}"
}
common.Alias() {
if [[ $# -ne 2 || -z $1 || -z $2 ]]; then
echo "$(common.GetAlias): The must be only two parameters of nonzero length" >&2
return 1;
fi
eval "alias $1='$2'"
local "f=${2##*common.}"
f="${f%%;*}"
local "v=common_$f"
f="common.$f"
if [[ -n ${!v:-} ]]; then
echo "$(common.GetAlias): $1: Function \`$f' already paired with name \`${!v}'" >&2
return 1;
fi
shopt -s expand_aliases
eval "$v=\"$1\""
}
common.ChangeAlias() {
if [[ $# -lt 1 || $# -gt 2 ]]; then
echo "usage: $(common.GetAlias) old_name [new_name]" >&2
return "1"
elif ! alias "$1" &>"/dev/null"; then
echo "$(common.GetAlias): $1: Name not found" >&2
return 1;
fi
local "s=$(alias "$1")"
s="${s#alias $1=\'}"
s="${s%\'}"
local "f=${s##*common.}"
f="${f%%;*}"
local "v=common_$f"
f="common.$f"
if [[ ${!v:-} != "$1" ]]; then
echo "$(common.GetAlias): $1: Name not paired with a function \`$f'" >&2
return 1;
elif [[ $# -gt 1 ]]; then
eval "alias $2='$s'"
eval "$v=\"$2\""
else
unset "$v"
fi
unalias "$1"
}
common.Alias exception 'common.Exception'
common.Alias throw 'common.Throw'
common.Alias try '{ if common.Try; then'
common.Alias yrt 'common.EchoExitStatus; fi; common.yrT; }'
common.Alias catch '{ while common.Catch'
common.Alias hctac 'common.hctaC -r; done; common.hctaC; }'
common.Alias finally '{ if common.Finally; then'
common.Alias yllanif 'fi; common.yllaniF; }'
common.Alias caught 'common.Caught'
common.Alias EchoExitStatus 'common.EchoExitStatus'
common.Alias EnableThrowOnError 'common.EnableThrowOnError'
common.Alias DisableThrowOnError 'common.DisableThrowOnError'
common.Alias GetStatus 'common.GetStatus'
common.Alias SetStatus 'common.SetStatus'
common.Alias GetMessage 'common.GetMessage'
common.Alias MessageCount 'common.MessageCount'
common.Alias CopyMessages 'common.CopyMessages'
common.Alias TryCatchFinally 'common.TryCatchFinally'
common.Alias DefaultErrHandler 'common.DefaultErrHandler'
common.Alias DefaultUnhandled 'common.DefaultUnhandled'
common.Alias CallStack 'common.CallStack'
common.Alias ChangeAlias 'common.ChangeAlias'
common.Alias TryCatchFinallyAlias 'common.Alias'
common.CallStack() {
local -i "i" "j" "k" "subshell=${2:-0}" "wi" "wl" "wn"
local "format= %*s %*s %-*s %s\n" "name"
eval local "lineno=('' ${BASH_LINENO[@]})"
for (( i=${1:-0},j=wi=wl=wn=0; i<${#FUNCNAME[@]}; ++i,++j )); do
name="$(common.GetAlias "$i")"
let "wi = ${#j} > wi ? wi = ${#j} : wi"
let "wl = ${#lineno[i]} > wl ? wl = ${#lineno[i]} : wl"
let "wn = ${#name} > wn ? wn = ${#name} : wn"
done
for (( i=${1:-0},j=0; i<${#FUNCNAME[@]}; ++i,++j )); do
! let "k = ${#FUNCNAME[@]} - i - 1"
name="$(common.GetAlias "$i")"
printf "$format" "$wi" "$j" "$wl" "${lineno[i]}" "$wn" "$name" "${BASH_SOURCE[i]}"
done
}
common.Echo() {
[[ $common_options != *d* ]] || echo "$@" >"$common_file"
}
common.DefaultErrHandler() {
echo "Orginal Status: $common_status"
echo "Exception Type: ERR"
}
common.Exception() {
common.TryCatchFinallyVerify || return
if [[ $# -eq 0 ]]; then
echo "$(common.GetAlias): At least one parameter is required" >&2
return "1"
elif [[ ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -lt 1 || 10#$1 -gt 255 ]]; then
echo "$(common.GetAlias): $1: First parameter was not an integer between 1 and 255" >&2
return "1"
fi
let "common_status = 10#$1"
shift
common_messages=()
for message in "$@"; do
common_messages+=("$message")
done
if [[ $common_options == *c* ]]; then
echo "Call Stack:" >"$common_fifo"
common.CallStack "2" >"$common_fifo"
fi
}
common.Throw() {
common.TryCatchFinallyVerify || return
local "message"
if ! common.TryCatchFinallyExists; then
echo "$(common.GetAlias): No Try-Catch-Finally exists" >&2
return "1"
elif [[ $# -eq 0 && common_status -eq 0 ]]; then
echo "$(common.GetAlias): No previous unhandled exception" >&2
return "1"
elif [[ $# -gt 0 && ( ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -lt 1 || 10#$1 -gt 255 ) ]]; then
echo "$(common.GetAlias): $1: First parameter was not an integer between 1 and 255" >&2
return "1"
fi
common.Echo -n "In Throw ?=$common_status "
common.Echo "try=$common_trySubshell subshell=$BASH_SUBSHELL #=$#"
if [[ $common_options == *k* ]]; then
common.CallStack "2" >"$common_file"
fi
if [[ $# -gt 0 ]]; then
let "common_status = 10#$1"
shift
for message in "$@"; do
echo "$message" >"$common_fifo"
done
if [[ $common_options == *c* ]]; then
echo "Call Stack:" >"$common_fifo"
common.CallStack "2" >"$common_fifo"
fi
elif [[ ${#common_messages[@]} -gt 0 ]]; then
for message in "${common_messages[@]}"; do
echo "$message" >"$common_fifo"
done
fi
chmod "0400" "$common_fifo"
common.Echo "Still in Throw $=$common_status subshell=$BASH_SUBSHELL #=$# -=$-"
exit "$common_status"
}
common.ErrHandler() {
common_status=$?
trap ERR
common.Echo -n "In ErrHandler ?=$common_status debug=$common_options "
common.Echo "try=$common_trySubshell subshell=$BASH_SUBSHELL order=$common_order"
if [[ -w "$common_fifo" ]]; then
if [[ $common_options != *e* ]]; then
common.Echo "ErrHandler is ignoring"
common_status="0"
return "$common_status" # value is ignored
fi
if [[ $common_options == *k* ]]; then
common.CallStack "2" >"$common_file"
fi
common.Echo "Calling ${common_errHandler:-}"
eval "${common_errHandler:-} \"${BASH_LINENO[0]}\" \"${BASH_SOURCE[1]}\" \"${FUNCNAME[1]}\" >$common_fifo <$common_fifo"
if [[ $common_options == *c* ]]; then
echo "Call Stack:" >"$common_fifo"
common.CallStack "2" >"$common_fifo"
fi
chmod "0400" "$common_fifo"
fi
common.Echo "Still in ErrHandler $=$common_status subshell=$BASH_SUBSHELL -=$-"
if [[ common_trySubshell -eq BASH_SUBSHELL ]]; then
return "$common_status" # value is ignored
else
exit "$common_status"
fi
}
common.Token() {
local "name"
case $1 in
b) name="before";;
t) name="$common_Try";;
y) name="$common_yrT";;
c) name="$common_Catch";;
h) name="$common_hctaC";;
f) name="$common_yllaniF";;
l) name="$common_Finally";;
*) name="unknown";;
esac
echo "$name"
}
common.TryCatchFinallyNext() {
common.ShellInit
local "previous=$common_order" "errmsg"
common_order="$2"
if [[ $previous != $1 ]]; then
errmsg="${BASH_SOURCE[2]}: line ${BASH_LINENO[1]}: syntax error_near unexpected token \`$(common.Token "$2")'"
echo "$errmsg" >&2
[[ /dev/fd/2 -ef $common_file ]] || echo "$errmsg" >"$common_file"
kill -s INT 0
return "1"
fi
}
common.ShellInit() {
if [[ common_initSubshell -ne BASH_SUBSHELL ]]; then
common_initSubshell="$BASH_SUBSHELL"
common_order="b"
fi
}
common.Try() {
common.TryCatchFinallyVerify || return
common.TryCatchFinallyNext "[byhl]" "t" || return
common_status="0"
common_subshell="$common_trySubshell"
common_trySubshell="$BASH_SUBSHELL"
common_messages=()
common.Echo "-------------> Setting try=$common_trySubshell at subshell=$BASH_SUBSHELL"
}
common.yrT() {
local "status=$?"
common.TryCatchFinallyVerify || return
common.TryCatchFinallyNext "[t]" "y" || return
common.Echo -n "Entered yrT ?=$status status=$common_status "
common.Echo "try=$common_trySubshell subshell=$BASH_SUBSHELL"
if [[ common_status -ne 0 ]]; then
common.Echo "Build message array. ?=$common_status, subshell=$BASH_SUBSHELL"
local "message=" "eof=TRY_CATCH_FINALLY_END_OF_MESSAGES_$RANDOM"
chmod "0600" "$common_fifo"
echo "$eof" >"$common_fifo"
common_messages=()
while read "message"; do
common.Echo "----> $message"
[[ $message != *$eof ]] || break
common_messages+=("$message")
done <"$common_fifo"
fi
common.Echo "In ytT status=$common_status"
common_trySubshell="$common_subshell"
}
common.Catch() {
common.TryCatchFinallyVerify || return
common.TryCatchFinallyNext "[yh]" "c" || return
[[ 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.TryCatchFinallyVerify || return
common.TryCatchFinallyNext "[c]" "h" || return
[[ $# -ne 1 || $1 != -r ]] || common_status="0"
}
common.Finally() {
common.TryCatchFinallyVerify || return
common.TryCatchFinallyNext "[ych]" "f" || return
}
common.yllaniF() {
common.TryCatchFinallyVerify || return
common.TryCatchFinallyNext "[f]" "l" || return
[[ common_status -eq 0 ]] || common.Throw
}
common.Caught() {
common.TryCatchFinallyVerify || return
[[ common_status -eq 0 ]] || return 1
}
common.EchoExitStatus() {
return "${1:-$?}"
}
common.EnableThrowOnError() {
common.TryCatchFinallyVerify || return
[[ $common_options == *e* ]] || common_options+="e"
}
common.DisableThrowOnError() {
common.TryCatchFinallyVerify || return
common_options="${common_options/e}"
}
common.GetStatus() {
common.TryCatchFinallyVerify || return
echo "$common_status"
}
common.SetStatus() {
common.TryCatchFinallyVerify || return
if [[ $# -ne 1 ]]; then
echo "$(common.GetAlias): $#: Wrong number of parameters" >&2
return "1"
elif [[ ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -lt 1 || 10#$1 -gt 255 ]]; then
echo "$(common.GetAlias): $1: First parameter was not an integer between 1 and 255" >&2
return "1"
fi
let "common_status = 10#$1"
}
common.GetMessage() {
common.TryCatchFinallyVerify || return
local "upper=${#common_messages[@]}"
if [[ upper -eq 0 ]]; then
echo "$(common.GetAlias): $1: There are no messages" >&2
return "1"
elif [[ $# -ne 1 ]]; then
echo "$(common.GetAlias): $#: Wrong number of parameters" >&2
return "1"
elif [[ ${#1} -gt 16 || -n ${1%%[0-9]*} || 10#$1 -ge upper ]]; then
echo "$(common.GetAlias): $1: First parameter was an invalid index" >&2
return "1"
fi
echo "${common_messages[$1]}"
}
common.MessageCount() {
common.TryCatchFinallyVerify || return
echo "${#common_messages[@]}"
}
common.CopyMessages() {
common.TryCatchFinallyVerify || return
if [[ $# -ne 1 ]]; then
echo "$(common.GetAlias): $#: Wrong number of parameters" >&2
return "1"
elif [[ ${#common_messages} -gt 0 ]]; then
eval "$1=(\"\${common_messages[@]}\")"
else
eval "$1=()"
fi
}
common.TryCatchFinallyExists() {
[[ ${common_fifo:-u} != u ]]
}
common.TryCatchFinallyVerify() {
local "name"
if ! common.TryCatchFinallyExists; then
echo "$(common.GetAlias "1"): No Try-Catch-Finally exists" >&2
return "2"
fi
}
common.GetOptions() {
local "opt"
local "name=$(common.GetAlias "1")"
if common.TryCatchFinallyExists; then
echo "$name: A Try-Catch-Finally already exists" >&2
return "1"
fi
let "OPTIND = 1"
let "OPTERR = 0"
while getopts ":cdeh:ko:u:v:" opt "$@"; do
case $opt in
c) [[ $common_options == *c* ]] || common_options+="c";;
d) [[ $common_options == *d* ]] || common_options+="d";;
e) [[ $common_options == *e* ]] || common_options+="e";;
h) common_errHandler="$OPTARG";;
k) [[ $common_options == *k* ]] || common_options+="k";;
o) common_file="$OPTARG";;
u) common_unhandled="$OPTARG";;
v) common_command="$OPTARG";;
\?) #echo "Invalid option: -$OPTARG" >&2
echo "$name: Illegal option: $OPTARG" >&2
return "1";;
:) echo "$name: Option requires an argument: $OPTARG" >&2
return "1";;
*) echo "$name: An error occurred while parsing options." >&2
return "1";;
esac
done
shift "$((OPTIND - 1))"
if [[ $# -lt 1 ]]; then
echo "$name: The fifo_file parameter is missing" >&2
return "1"
fi
common_fifo="$1"
if [[ ! -p $common_fifo ]]; then
echo "$name: $1: The fifo_file is not an open FIFO" >&2
return "1"
fi
shift
if [[ $# -lt 1 ]]; then
echo "$name: The function parameter is missing" >&2
return "1"
fi
common_function="$1"
if ! chmod "0600" "$common_fifo"; then
echo "$name: $common_fifo: Can not change file mode to 0600" >&2
return "1"
fi
local "message=" "eof=TRY_CATCH_FINALLY_END_OF_FILE_$RANDOM"
{ echo "$eof" >"$common_fifo"; } 2>"/dev/null"
if [[ $? -ne 0 ]]; then
echo "$name: $common_fifo: Can not write" >&2
return "1"
fi
{ while [[ $message != *$eof ]]; do
read "message"
done <"$common_fifo"; } 2>"/dev/null"
if [[ $? -ne 0 ]]; then
echo "$name: $common_fifo: Can not read" >&2
return "1"
fi
return "0"
}
common.DefaultUnhandled() {
local -i "i"
echo "-------------------------------------------------"
echo "$(common.GetAlias "common.TryCatchFinally"): Unhandeled exception occurred"
echo "Status: $(GetStatus)"
echo "Messages:"
for ((i=0; i<$(MessageCount); i++)); do
echo "$(GetMessage "$i")"
done
echo "-------------------------------------------------"
}
common.TryCatchFinally() {
local "common_file=/dev/fd/2"
local "common_errHandler=common.DefaultErrHandler"
local "common_unhandled=common.DefaultUnhandled"
local "common_options="
local "common_fifo="
local "common_function="
local "common_flags=$-"
local "common_trySubshell=-1"
local "common_initSubshell=-1"
local "common_subshell"
local "common_status=0"
local "common_order=b"
local "common_command="
local "common_messages=()"
local "common_handler=$(trap -p ERR)"
[[ -n $common_handler ]] || common_handler="trap ERR"
common.GetOptions "$@" || return "$?"
shift "$((OPTIND + 1))"
[[ -z $common_command ]] || common_command+="=$"
common_command+='("$common_function" "$@")'
set -E
set +e
trap "common.ErrHandler" ERR
if true; then
common.Try
eval "$common_command"
common.EchoExitStatus
common.yrT
fi
while common.Catch; do
"$common_unhandled" >&2
break
common.hctaC -r
done
common.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"
return "$((common_status?2:0))"
}
我已经在bash中开发了一个几乎完美无缺的try & catch实现,它允许您编写如下代码:
try
echo 'Hello'
false
echo 'This will not be displayed'
catch
echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
您甚至可以将try-catch块嵌套在它们自己内部!
try {
echo 'Hello'
try {
echo 'Nested Hello'
false
echo 'This will not execute'
} catch {
echo "Nested Caught (@ $__EXCEPTION_LINE__)"
}
false
echo 'This will not execute too'
} catch {
echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
}
代码是bash样板文件/框架的一部分。它进一步扩展了try & catch的思想,例如使用回溯和异常进行错误处理(以及其他一些不错的特性)。
下面是负责try & catch的代码:
set -o pipefail
shopt -s expand_aliases
declare -ig __oo__insideTryCatch=0
# if try-catch is nested, then set +e before so the parent handler doesn't catch us
alias try="[[ \$__oo__insideTryCatch -gt 0 ]] && set +e;
__oo__insideTryCatch+=1; ( set -e;
trap \"Exception.Capture \${LINENO}; \" ERR;"
alias catch=" ); Exception.Extract \$? || "
Exception.Capture() {
local script="${BASH_SOURCE[1]#./}"
if [[ ! -f /tmp/stored_exception_source ]]; then
echo "$script" > /tmp/stored_exception_source
fi
if [[ ! -f /tmp/stored_exception_line ]]; then
echo "$1" > /tmp/stored_exception_line
fi
return 0
}
Exception.Extract() {
if [[ $__oo__insideTryCatch -gt 1 ]]
then
set -e
fi
__oo__insideTryCatch+=-1
__EXCEPTION_CATCH__=( $(Exception.GetLastException) )
local retVal=$1
if [[ $retVal -gt 0 ]]
then
# BACKWARDS COMPATIBILE WAY:
# export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-1)]}"
# export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-2)]}"
export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[-1]}"
export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[-2]}"
export __EXCEPTION__="${__EXCEPTION_CATCH__[@]:0:(${#__EXCEPTION_CATCH__[@]} - 2)}"
return 1 # so that we may continue with a "catch"
fi
}
Exception.GetLastException() {
if [[ -f /tmp/stored_exception ]] && [[ -f /tmp/stored_exception_line ]] && [[ -f /tmp/stored_exception_source ]]
then
cat /tmp/stored_exception
cat /tmp/stored_exception_line
cat /tmp/stored_exception_source
else
echo -e " \n${BASH_LINENO[1]}\n${BASH_SOURCE[2]#./}"
fi
rm -f /tmp/stored_exception /tmp/stored_exception_line /tmp/stored_exception_source
return 0
}
你可以自由使用,分叉和贡献——它在GitHub上。
Bash不会在检测到错误状态时中止正在运行的执行(除非您设置了-e标志)。提供try/catch的编程语言这样做是为了抑制由于这种特殊情况而产生的“保释”(因此通常称为“异常”)。
相反,在bash中,只有有问题的命令将退出,退出码大于0,表示错误状态。当然,您可以检查这一点,但由于没有任何自动保释,因此try/catch没有意义。它只是缺乏背景。
然而,你可以通过使用子壳来模拟一个保释,它可以在你决定的点终止:
(
echo "Do one thing"
echo "Do another thing"
if some_condition
then
exit 3 # <-- this is our simulated bailing out
fi
echo "Do yet another thing"
echo "And do a last thing"
) # <-- here we arrive after the simulated bailing out, and $? will be 3 (exit code)
if [ $? = 3 ]
then
echo "Bail out detected"
fi
而不是some_condition的if你也可以尝试一个命令,如果它失败(退出码大于0),保释:
(
echo "Do one thing"
echo "Do another thing"
some_command || exit 3
echo "Do yet another thing"
echo "And do a last thing"
)
...
不幸的是,使用这种技术只能使用255个不同的退出码(1..255),而且不能使用像样的异常对象。
如果你需要更多的信息来传递模拟异常,你可以使用subshell的stdout,但这有点复杂,可能是另一个问题;-)
使用上面提到的-e标志,你甚至可以剥离显式的退出语句:
(
set -e
echo "Do one thing"
echo "Do another thing"
some_command
echo "Do yet another thing"
echo "And do a last thing"
)
...