Two functions are provided to enable zsh to provide exception handling in a form that should be familiar from other languages.
throw
exceptionThe function throw
throws the named exception. The name is
an arbitrary string and is only used by the throw
and catch
functions. An exception is for the most part treated the same as a
shell error, i.e. an unhandled exception will cause the shell to abort all
processing in a function or script and to return to the top level in an
interactive shell.
catch
exception-patternThe function catch
returns status zero if an exception was thrown and
the pattern exception-pattern matches its name. Otherwise it
returns status 1. exception-pattern is a standard
shell pattern, respecting the current setting of the EXTENDED_GLOB
option. An alias catch
is also defined to prevent the argument to the
function from matching filenames, so patterns may be used unquoted. Note
that as exceptions are not fundamentally different from other shell errors
it is possible to catch shell errors by using an empty string as the
exception name. The shell variable CAUGHT
is set by catch
to the
name of the exception caught. It is possible to rethrow an exception by
calling the throw
function again once an exception has been caught.
The functions are designed to be used together with the always
construct
described in
Complex Commands. This is important as only this
construct provides the required support for exceptions. A typical example
is as follows.
{ # "try" block # ... nested code here calls "throw MyExcept" } always { # "always" block if catch MyExcept; then print "Caught exception MyExcept" elif catch {No value for `dsq'}; then print "Caught a shell error. Propagating..." throw {No value for `dsq'} fi # Other exceptions are not handled but may be caught further # up the call stack. }
If all exceptions should be caught, the following idiom might be preferable.
{ # ... nested code here throws an exception } always { if catch *; then case $CAUGHT in (MyExcept) print "Caught my own exception" ;; (*) print "Caught some other exception" ;; esac fi }
In common with exception handling in other languages, the exception may be thrown by code deeply nested inside the ‘try’ block. However, note that it must be thrown inside the current shell, not in a subshell forked for a pipeline, parenthesised current-shell construct, or some form of command or process substitution.
The system internally uses the shell variable EXCEPTION
to record the
name of the exception between throwing and catching. One drawback of this
scheme is that if the exception is not handled the variable EXCEPTION
remains set and may be incorrectly recognised as the name of an exception
if a shell error subsequently occurs. Adding unset EXCEPTION
at the
start of the outermost layer of any code that uses exception handling will
eliminate this problem.