There are a couple more ways with which you can approach this problem. Assuming one of your requirement is to run a shell script/function containing a few shell commands and check if the script ran successfully and throw errors in case of failures.
The shell commands in generally rely on exit-codes returned to let the shell know if it was successful or failed due to some unexpected events.
So what you want to do falls upon these two categories
- exit on error
- exit and clean-up on error
Depending on which one you want to do, there are shell options available to use. For the first case, the shell provides an option with set -e
and for the second you could do a trap
on EXIT
Should I use exit
in my script/function?
Using exit
generally enhances readability In certain routines, once you know the answer, you want to exit to the calling routine immediately. If the routine is defined in such a way that it doesn’t require any further cleanup once it detects an error, not exiting immediately means that you have to write more code.
So in cases if you need to do clean-up actions on script to make the termination of the script clean, it is preferred to not to use exit
.
Should I use set -e
for error on exit?
No!
set -e
was an attempt to add «automatic error detection» to the shell. Its goal was to cause the shell to abort any time an error occurred, but it comes with a lot of potential pitfalls for example,
-
The commands that are part of an if test are immune. In the example, if you expect it to break on the
test
check on the non-existing directory, it wouldn’t, it goes through to the else conditionset -e f() { test -d nosuchdir && echo no dir; } f echo survived
-
Commands in a pipeline other than the last one, are immune. In the example below, because the most recently executed (rightmost) command’s exit code is considered (
cat
) and it was successful. This could be avoided by setting by theset -o pipefail
option but its still a caveat.set -e somecommand that fails | cat - echo survived
Recommended for use — trap
on exit
The verdict is if you want to be able to handle an error instead of blindly exiting, instead of using set -e
, use a trap
on the ERR
pseudo signal.
The ERR
trap is not to run code when the shell itself exits with a non-zero error code, but when any command run by that shell that is not part of a condition (like in if cmd
, or cmd ||
) exits with a non-zero exit status.
The general practice is we define an trap handler to provide additional debug information on which line and what cause the exit. Remember the exit code of the last command that caused the ERR
signal would still be available at this point.
cleanup() {
exitcode=$?
printf 'error condition hitn' 1>&2
printf 'exit code returned: %sn' "$exitcode"
printf 'the command executing at the time of the error was: %sn' "$BASH_COMMAND"
printf 'command present on line: %d' "${BASH_LINENO[0]}"
# Some more clean up code can be added here before exiting
exit $exitcode
}
and we just use this handler as below on top of the script that is failing
trap cleanup ERR
Putting this together on a simple script that contained false
on line 15, the information you would be getting as
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
The trap
also provides options irrespective of the error to just run the cleanup on shell completion (e.g. your shell script exits), on signal EXIT
. You could also trap on multiple signals at the same time. The list of supported signals to trap on can be found on the trap.1p — Linux manual page
Another thing to notice would be to understand that none of the provided methods work if you are dealing with sub-shells are involved in which case, you might need to add your own error handling.
-
On a sub-shell with
set -e
wouldn’t work. Thefalse
is restricted to the sub-shell and never gets propagated to the parent shell. To do the error handling here, add your own logic to do(false) || false
set -e (false) echo survived
-
The same happens with
trap
also. The logic below wouldn’t work for the reasons mentioned above.trap 'echo error' ERR (false)
There are a couple more ways with which you can approach this problem. Assuming one of your requirement is to run a shell script/function containing a few shell commands and check if the script ran successfully and throw errors in case of failures.
The shell commands in generally rely on exit-codes returned to let the shell know if it was successful or failed due to some unexpected events.
So what you want to do falls upon these two categories
- exit on error
- exit and clean-up on error
Depending on which one you want to do, there are shell options available to use. For the first case, the shell provides an option with set -e
and for the second you could do a trap
on EXIT
Should I use exit
in my script/function?
Using exit
generally enhances readability In certain routines, once you know the answer, you want to exit to the calling routine immediately. If the routine is defined in such a way that it doesn’t require any further cleanup once it detects an error, not exiting immediately means that you have to write more code.
So in cases if you need to do clean-up actions on script to make the termination of the script clean, it is preferred to not to use exit
.
Should I use set -e
for error on exit?
No!
set -e
was an attempt to add «automatic error detection» to the shell. Its goal was to cause the shell to abort any time an error occurred, but it comes with a lot of potential pitfalls for example,
-
The commands that are part of an if test are immune. In the example, if you expect it to break on the
test
check on the non-existing directory, it wouldn’t, it goes through to the else conditionset -e f() { test -d nosuchdir && echo no dir; } f echo survived
-
Commands in a pipeline other than the last one, are immune. In the example below, because the most recently executed (rightmost) command’s exit code is considered (
cat
) and it was successful. This could be avoided by setting by theset -o pipefail
option but its still a caveat.set -e somecommand that fails | cat - echo survived
Recommended for use — trap
on exit
The verdict is if you want to be able to handle an error instead of blindly exiting, instead of using set -e
, use a trap
on the ERR
pseudo signal.
The ERR
trap is not to run code when the shell itself exits with a non-zero error code, but when any command run by that shell that is not part of a condition (like in if cmd
, or cmd ||
) exits with a non-zero exit status.
The general practice is we define an trap handler to provide additional debug information on which line and what cause the exit. Remember the exit code of the last command that caused the ERR
signal would still be available at this point.
cleanup() {
exitcode=$?
printf 'error condition hitn' 1>&2
printf 'exit code returned: %sn' "$exitcode"
printf 'the command executing at the time of the error was: %sn' "$BASH_COMMAND"
printf 'command present on line: %d' "${BASH_LINENO[0]}"
# Some more clean up code can be added here before exiting
exit $exitcode
}
and we just use this handler as below on top of the script that is failing
trap cleanup ERR
Putting this together on a simple script that contained false
on line 15, the information you would be getting as
error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15
The trap
also provides options irrespective of the error to just run the cleanup on shell completion (e.g. your shell script exits), on signal EXIT
. You could also trap on multiple signals at the same time. The list of supported signals to trap on can be found on the trap.1p — Linux manual page
Another thing to notice would be to understand that none of the provided methods work if you are dealing with sub-shells are involved in which case, you might need to add your own error handling.
-
On a sub-shell with
set -e
wouldn’t work. Thefalse
is restricted to the sub-shell and never gets propagated to the parent shell. To do the error handling here, add your own logic to do(false) || false
set -e (false) echo survived
-
The same happens with
trap
also. The logic below wouldn’t work for the reasons mentioned above.trap 'echo error' ERR (false)
Basic error handling
If your test case runner returns a non-zero code for failed tests, you can simply write:
test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
printf '%sn' "Test case x failed" >&2 # write error message to stderr
exit 1 # or exit $test_result
fi
Or even shorter:
if ! test_handler test_case_x; then
printf '%sn' "Test case x failed" >&2
exit 1
fi
Or the shortest:
test_handler test_case_x || { printf '%sn' "Test case x failed" >&2; exit 1; }
To exit with test_handler’s exit code:
test_handler test_case_x || { ec=$?; printf '%sn' "Test case x failed" >&2; exit $ec; }
Advanced error handling
If you want to take a more comprehensive approach, you can have an error handler:
exit_if_error() {
local exit_code=$1
shift
[[ $exit_code ]] && # do nothing if no error code passed
((exit_code != 0)) && { # do nothing if error code is 0
printf 'ERROR: %sn' "[email protected]" >&2 # we can use better logging here
exit "$exit_code" # we could also check to make sure
# error code is numeric when passed
}
}
then invoke it after running your test case:
run_test_case test_case_x
exit_if_error $? "Test case x failed"
or
run_test_case test_case_x || exit_if_error $? "Test case x failed"
The advantages of having an error handler like exit_if_error
are:
- we can standardize all the error handling logic such as logging, printing a stack trace, notification, doing cleanup etc., in one place
- by making the error handler get the error code as an argument, we can spare the caller from the clutter of
if
blocks that test exit codes for errors - if we have a signal handler (using trap), we can invoke the error handler from there
Error handling and logging library
Here is a complete implementation of error handling and logging:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Related posts
- Error handling in Bash
- The ‘caller’ builtin command on Bash Hackers Wiki
- Are there any standard exit status codes in Linux?
- BashFAQ/105 — Why doesn’t set -e (or set -o errexit, or trap ERR) do what I expected?
- Equivalent of
__FILE__
,__LINE__
in Bash - Is there a TRY CATCH command in Bash
- To add a stack trace to the error handler, you may want to look at this post: Trace of executed programs called by a Bash script
- Ignoring specific errors in a shell script
- Catching error codes in a shell pipe
- How do I manage log verbosity inside a shell script?
- How to log function name and line number in Bash?
- Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash?
This depends on where you want the error message be stored.
You can do the following:
echo "Error!" > logfile.log
exit 125
Or the following:
echo "Error!" 1>&2
exit 64
When you raise an exception you stop the program’s execution.
You can also use something like exit xxx
where xxx
is the error code you may want to return to the operating system (from 0 to 255). Here 125
and 64
are just random codes you can exit with. When you need to indicate to the OS that the program stopped abnormally (eg. an error occurred), you need to pass a non-zero exit code to exit
.
As @chepner pointed out, you can do exit 1
, which will mean an unspecified error.
Star
Embed
What would you like to do?
Catching and throwing errors in bash
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
###### ERROR TRAP — DONT WRITE OTHER COMMANDS BEFORE THIS ############ | |
set -eEx | |
function HANDLE_ERROR() { | |
echo «SCRIPT FAILED« | |
echo «FAILED AT: $(caller)« | |
# Do any other commands here | |
exit 1 | |
} | |
# Install trap | |
trap ‘HANDLE_ERROR‘ ERR | |
########################## ERROR TRAP ################################# | |
do_something() { | |
# Throw err example: | |
if myCmdFails then HANDLE_ERROR fi | |
} | |
do_something_else() { | |
myOtherCmd | |
# if this cmd fails, trap will catch it automatically | |
} |
To expand on the @Gilles’ answer:
Indeed, set -e
doesn’t work inside commands if you use ||
operator after them, even if you run them in a subshell; e.g., this wouldn’t work:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_1.sh: line 16: some_failed_command: command not found
# <-- inner
# <-- outer
set -e
outer() {
echo '--> outer'
(inner) || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
But ||
operator is needed to prevent returning from the outer function before cleanup.
There is a little trick that can be used to fix this: run the inner command in background, and then immediately wait for it. The wait
builtin will return the exit code of the inner command, and now you’re using ||
after wait
, not the inner function, so set -e
works properly inside the latter:
#!/bin/sh
# prints:
#
# --> outer
# --> inner
# ./so_2.sh: line 27: some_failed_command: command not found
# --> cleanup
set -e
outer() {
echo '--> outer'
inner &
wait $! || {
exit_code=$?
echo '--> cleanup'
return $exit_code
}
echo '<-- outer'
}
inner() {
set -e
echo '--> inner'
some_failed_command
echo '<-- inner'
}
outer
Here is the generic function that builds upon this idea. It should work in all POSIX-compatible shells if you remove local
keywords, i.e. replace all local x=y
with just x=y
:
# [CLEANUP=cleanup_cmd] run cmd [args...]
#
# `cmd` and `args...` A command to run and its arguments.
#
# `cleanup_cmd` A command that is called after cmd has exited,
# and gets passed the same arguments as cmd. Additionally, the
# following environment variables are available to that command:
#
# - `RUN_CMD` contains the `cmd` that was passed to `run`;
# - `RUN_EXIT_CODE` contains the exit code of the command.
#
# If `cleanup_cmd` is set, `run` will return the exit code of that
# command. Otherwise, it will return the exit code of `cmd`.
#
run() {
local cmd="$1"; shift
local exit_code=0
local e_was_set=1; if ! is_shell_attribute_set e; then
set -e
e_was_set=0
fi
"$cmd" "$@" &
wait $! || {
exit_code=$?
}
if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then
set +e
fi
if [ -n "$CLEANUP" ]; then
RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@"
return $?
fi
return $exit_code
}
is_shell_attribute_set() { # attribute, like "x"
case "$-" in
*"$1"*) return 0 ;;
*) return 1 ;;
esac
}
Example of usage:
#!/bin/sh
set -e
# Source the file with the definition of `run` (previous code snippet).
# Alternatively, you may paste that code directly here and comment the next line.
. ./utils.sh
main() {
echo "--> main: $@"
CLEANUP=cleanup run inner "$@"
echo "<-- main"
}
inner() {
echo "--> inner: $@"
sleep 0.5; if [ "$1" = 'fail' ]; then
oh_my_god_look_at_this
fi
echo "<-- inner"
}
cleanup() {
echo "--> cleanup: $@"
echo " RUN_CMD = '$RUN_CMD'"
echo " RUN_EXIT_CODE = $RUN_EXIT_CODE"
sleep 0.3
echo '<-- cleanup'
return $RUN_EXIT_CODE
}
main "$@"
Running the example:
$ ./so_3 fail; echo "exit code: $?"
--> main: fail
--> inner: fail
./so_3: line 15: oh_my_god_look_at_this: command not found
--> cleanup: fail
RUN_CMD = 'inner'
RUN_EXIT_CODE = 127
<-- cleanup
exit code: 127
$ ./so_3 pass; echo "exit code: $?"
--> main: pass
--> inner: pass
<-- inner
--> cleanup: pass
RUN_CMD = 'inner'
RUN_EXIT_CODE = 0
<-- cleanup
<-- main
exit code: 0
The only thing that you need to be aware of when using this method is that all modifications of Shell variables done from the command you pass to run
will not propagate to the calling function, because the command runs in a subshell.
Exception handling syntax is the set of keywords and/or structures provided by a computer programming language to allow exception handling, which separates the handling of errors that arise during a program’s operation from its ordinary processes. Syntax for exception handling varies between programming languages, partly to cover semantic differences but largely to fit into each language’s overall syntactic structure. Some languages do not call the relevant concept «exception handling»; others may not have direct facilities for it, but can still provide means to implement it.
Most commonly, error handling uses a try...[catch...][finally...]
block, and errors are created via a throw
statement, but there is significant variation in naming and syntax.
Catalogue of exception handling syntaxes[edit]
Ada[edit]
- Exception declarations
- Raising exceptions
raise Some_Error; raise Some_Error with "Out of memory"; -- specific diagnostic message
- Exception handling and propagation
with Ada.Exceptions, Ada.Text_IO; procedure Foo is Some_Error : exception; begin Do_Something_Interesting; exception -- Start of exception handlers when Constraint_Error => ... -- Handle constraint error when Storage_Error => -- Propagate Storage_Error as a different exception with a useful message raise Some_Error with "Out of memory"; when Error : others => -- Handle all others Ada.Text_IO.Put("Exception: "); Ada.Text_IO.Put_Line(Ada.Exceptions.Exception_Name(Error)); Ada.Text_IO.Put_Line(Ada.Exceptions.Exception_Message(Error)); end Foo;
Assembly language[edit]
Most assembly languages will have a macro instruction or an interrupt address available for the particular system to intercept events such as illegal op codes, program check, data errors, overflow, divide by zero, and other such. IBM and Univac mainframes had the STXIT macro. Digital Equipment Corporation RT11 systems had trap vectors for program errors, i/o interrupts, and such. DOS has certain interrupt addresses. Microsoft Windows has specific module calls to trap program errors.
Bash[edit]
#!/usr/bin/env bash #set -e provides another error mechanism print_error(){ echo "there was an error" } trap print_error exit #list signals to trap tempfile=`mktemp` trap "rm $tempfile" exit ./other.sh || echo warning: other failed echo oops) echo never printed
One can set a trap for multiple errors, responding to any signal with syntax like:
trap 'echo Error at line ${LINENO}' ERR
BASIC[edit]
Further information: BASIC
An On Error goto/gosub structure is used in BASIC and is quite different from modern exception handling; in BASIC there is only one global handler whereas in modern exception handling, exception handlers are stacked.
ON ERROR GOTO handler OPEN "Somefile.txt" FOR INPUT AS #1 CLOSE #1 PRINT "File opened successfully" END handler: PRINT "File does not exist" END ' RESUME may be used instead which returns control to original position.
C[edit]
C does not provide direct support to exception handling: it is the programmer’s responsibility to prevent errors in the first place and test return values from the functions.
In any case, a possible way to implement exception handling in standard C is to use setjmp/longjmp functions:
#include <setjmp.h> #include <stdio.h> #include <stdlib.h> enum { SOME_EXCEPTION = 1 } exception; jmp_buf state; int main(void) { if (!setjmp(state)) // try { if (/* something happened */) { exception = SOME_EXCEPTION; longjmp(state, 0); // throw SOME_EXCEPTION } } else switch(exception) { case SOME_EXCEPTION: // catch SOME_EXCEPTION puts("SOME_EXCEPTION caught"); break; default: // catch ... puts("Some strange exception"); } return EXIT_SUCCESS; }
Microsoft-specific[edit]
Two types exist:
- Structured Exception Handling (SEH)
- Vectored Exception Handling (VEH, introduced in Windows XP)
Example of SEH in C programming language:
int filterExpression (EXCEPTION_POINTERS* ep) { ep->ContextRecord->Eip += 8; // divide instruction may be encoded from 2 to 8 bytes return EXCEPTION_CONTINUE_EXECUTION; } int main(void) { static int zero; __try { zero = 1/zero; __asm { nop nop nop nop nop nop nop } printf ("Past the exception.n"); } __except (filterExpression (GetExceptionInformation())) { printf ("Handler called.n"); } return 0; }
C#[edit]
A try
block must have at least one catch
or finally
clause and at most one finally
clause.
public static void Main() { try { // Code that could throw an exception. } catch (HttpException ex) { // Handles a HttpException. The exception object is stored in "ex". } catch (Exception) { // Handles any CLR exception that is not a HttpException. // Since the exception has not been given an identifier, it cannot be referenced. } catch { // Handles anything that might be thrown, including non-CLR exceptions. } finally { // Always run when leaving the try block (including catch clauses), regardless of whether any exceptions were thrown or whether they were handled. // Often used to clean up and close resources such a file handles. // May not be run when Environment.FailFast() is called and in other system-wide exceptional conditions (e.g. power loss), or when the process crashes due to an exception in another thread. } }
C++[edit]
Further information: C++
#include <exception> int main() { try { // do something (might throw an exception) } catch (const std::exception& e) { // handle exception e } catch (...) { // catches all exceptions, not already caught by a catch block before // can be used to catch exception of unknown or irrelevant type } }
In C++, a resource acquisition is initialization technique can be used to clean up resources in exceptional situations. C++ intentionally does not support finally
.[1] The outer braces for the method are optional.
ColdFusion Markup Language (CFML)[edit]
Script syntax[edit]
<cfscript> try { //throw CF9+ throw(type="TypeOfException", message="Oops", detail="xyz"); // alternate throw syntax: throw "Oops"; // this equivalent to the "message" value in the above example } catch (any e) { writeOutput("Error: " & e.message); rethrow; //CF9+ } finally { //CF9+ writeOutput("I run even if no error"); } </cfscript>
Adobe ColdFusion documentation[2]
Tag syntax[edit]
<cftry> code that may cause an exception <cfcatch ...> <cftry> First level of exception handling code <cfcatch ...> Second level of exception handling code </cfcatch> <cffinally> final code </cffinally> </cftry> </cfcatch> </cftry>
Adobe ColdFusion documentation[3]
Railo-Lucee specific syntax[edit]
Added to the standard syntax above, CFML dialects of Railo and Lucee allow a retry
statement.[4]
This statement returns processing to the start of the prior try
block.
CFScript example:
try { // code which could result in an exception } catch (any e){ retry; }
Tag-syntax example:
<cftry> <!--- code which could result in an exception ---> <cfcatch> <cfretry> </cfcatch> </cftry>
D[edit]
import std.stdio; // for writefln() int main() { try { // do something that might throw an exception } catch (FooException e) { // handle exceptions of type FooException } catch (Object o) { // handle any other exceptions writefln("Unhandled exception: ", o); return 1; } return 0; }
In D, a finally
clause or the resource acquisition is initialization technique can be used to clean up resources in exceptional situations.
Delphi[edit]
- Exception declarations
type ECustom = class(Exception) // Exceptions are children of the class Exception. private FCustomData: SomeType; // Exceptions may have custom extensions. public constructor CreateCustom(Data: SomeType); // Needs an implementation property CustomData: SomeType read FCustomData; end;
- Raising exceptions
raise Exception.Create('Message'); raise Exception.CreateFmt('Message with values: %d, %d',[value1, value2]); // See SysUtils.Format() for parameters. raise ECustom.CreateCustom(X);
- Exception handling and propagation[5]
try // For finally. try // For except. ... // Code that may raise an exception. except on C:ECustom do begin ... // Handle ECustom. ... if Predicate(C.CustomData) then ... end; on S:ESomeOtherException do begin // Propagate as an other exception. raise EYetAnotherException.Create(S.Message); end; on E:Exception do begin ... // Handle other exceptions. raise; // Propagate. end; end; finally // Code to execute whether or not an exception is raised (e.g., clean-up code). end;
Erlang[edit]
try % some dangerous code catch throw:{someError, X} -> ok; % handle an exception error:X -> ok; % handle another exception _:_ -> ok % handle all exceptions after % clean up end
F#[edit]
In addition to the OCaml-based try...with
, F# also has the separate try...finally
construct, which has the same behavior as a try block with a finally
clause in other .NET languages.
For comparison, this is a translation of the C# sample above.
try try () (* Code that could throw an exception. *) with | :? System.Net.WebException as ex -> () (* Handles a WebException. The exception object is stored in "ex". *) | :? exn -> () (* Handles any CLR exception. Since the exception has not been given an identifier, it cannot be referenced. *) | _ -> () (* Handles anything that might be thrown, including non-CLR exceptions. *) finally () (* Always run when leaving the try block, regardless of whether any exceptions were thrown or whether they were handled. Often used to clean up and close resources such a file handles. May not be run when Environment.FailFast() is called and in other system-wide exceptional conditions (e.g. power loss), or when the process crashes due to an exception in another thread. *)
For comparison, this is translation of the OCaml sample below.
exception MyException of string * int (* exceptions can carry a value *) let _ = try raise (MyException ("not enough food", 2)); printfn "Not reached" with | MyException (s, i) -> printf "MyException: %s, %dn" s i | e -> (* catch all exceptions *) eprintf "Unexpected exception : %O" e; eprintf "%O" e.StackTrace
Haskell[edit]
Haskell does not have special syntax for exceptions. Instead, a try
/catch
/finally
/etc
. interface is provided by functions.
import Prelude hiding(catch) import Control.Exception instance Exception Int instance Exception Double main = do catch (catch (throw (42::Int)) (e-> print (0,e::Double))) (e-> print (1,e::Int))
prints
(1,42)
in analogy with this C++
#include <iostream> using namespace std; int main() { try {throw (int)42;} catch(double e) {cout << "(0," << e << ")" << endl;} catch(int e) {cout << "(1," << e << ")" << endl;} }
Another example is
do { -- Statements in which errors might be thrown } `catch` ex -> do { -- Statements that execute in the event of an exception, with 'ex' bound to the exception }
In purely functional code, if only one error condition exists, the Maybe
type may be sufficient, and is an instance of Haskell’s Monad
class by default. More complex error propagation can be achieved using the Error
or ErrorT
monads, for which similar functionality (using `catch`
) is supported.
Java[edit]
A try
block must have at least one catch
or finally
clause and at most one finally
clause.
try { // Normal execution path. throw new EmptyStackException(); } catch (ExampleException ee) { // Deal with the ExampleException. } finally { // Always run when leaving the try block (including finally clauses), regardless of whether any exceptions were thrown or whether they were handled. // Often used to clean up and close resources such a file handles. // May not be run when System.exit() is called and in other system-wide exceptional conditions (e.g. power loss). }
JavaScript[edit]
The design of JavaScript makes loud/hard errors very uncommon. Soft/quiet errors are much more prevalent. Hard errors propagate to the nearest try
statement, which must be followed by either a single catch
clause, a single finally
clause, or both.
try { // Statements in which exceptions might be thrown throw new Error("error"); } catch(error) { // Statements that execute in the event of an exception } finally { // Statements that execute afterward either way }
If there is no try
statement at all, then the webpage does not crash. Rather, an error is logged to the console and the stack is cleared. However, JavaScript has the interesting quirk of asynchronous externally-invoked entry points. Whereas, in most other languages, there is always some part of the code running at all times, JavaScript does not have to run linearly from start to end. For example, event listeners, Promises, and timers can be invoked by the browser at a later point in time and run in an isolated but shared context with the rest of the code. Observe how the code below will throw a new error every 4 seconds for an indefinite period of time or until the browser/tab/computer is closed.
setInterval(function() { throw new Error("Example of an error thrown on a 4 second interval."); }, 4000);
Another interesting quirk is polymorphism: JavaScript can throw primitive values as errors.
try { throw 12345; // primitive number } catch(error) { console.log(error); // logs 12345 as a primitive number to the console }
Note that the catch
clause is a catch-all, which catches every type of error. There is no syntaxical ability to assign different handlers to different error types aside from experimental and presently removed Gecko extensions from many years ago. Instead, one can either propagate the error by using a throw
statement inside the catch
statement, or use multiple conditional cases. Let us compare an example in Java and its rough equivalents in JavaScript.
// Example in Java try { Integer i = null; i.intValue(); // throws a NullPointerException } catch(NullPointerException error) { // Variable might be null } catch(ArithmeticException error) { // Handle problems with numbers }
// Approximation #1 in JavaScript try { // Statements in which exceptions might be thrown var example = null; example.toString(); } catch(error) { if (error.type === "TypeError") { // Variable might be null } else if (error.type === "RangeError") { // Handle problems with numbers } }
// Approximation #2 in JavaScript try { try { // Statements in which exceptions might be thrown var example = null; example.toString(); } catch(error) { if (error.type !== "TypeError") throw error; // Variable might be null } } catch(error) { if (error.type !== "RangeError") throw error; // Handle problems with numbers }
Another aspect of exceptions are promises, which handle the exception asynchronously. Handling the exception asynchronously has the benefit that errors inside the error handler do not propagate further outwards.
new Promise(function() { throw new Error("Example error!"); }).catch(function(err) { console.log("Caught ", err); });
Also observe how event handlers can tie into promises as well.
addEventListener("unhandledrejection", function(event) { console.log(event.reason); event.preventDefault(); //prevent logging the error via console.error to the console--the default behavior }); new Promise(function() { throw new Error("Example error!"); });
Lastly, note that, as JavaScript uses mark-and-sweep garbage-collection, there is never any memory leakage from throw statements because the browser automatically cleans dead objects—even with circular references.
try { // Statements in which exceptions might be thrown const obj = {}; obj.selfPropExample = obj; // circular reference throw obj; } catch(error) { // Statements that execute in the event of an exception }
Lisp[edit]
Common Lisp[edit]
(ignore-errors (/ 1 0)) (handler-case (progn (print "enter an expression") (eval (read))) (error (e) (print e))) (unwind-protect (progn (print "enter an expression") (eval (read))) (print "This print will always be executed, similar to finally."))
Lua[edit]
Lua uses the pcall
and xpcall
functions, with xpcall
taking a function to act as a catch
block.
- Predefined function
function foo(x) if x then return x else error "Not a true value" end end function attempt(arg) success, value = pcall(foo, arg) if not success then print("Error: " .. tostring(value)) else print("Returned: " .. tostring(value)) end end attempt("hello") -- Returned: hello attempt(nil) -- Error: stdin:5: Not a true value attempt({}) -- Returned: table: 00809308 if foo(42) then print "Success" end -- Success
- Anonymous function
if pcall( function() -- Do something that might throw an error. end) then print "No errors" -- Executed if the protected call was successful. else print "Error encountered" -- Executed if the protected call failed. end print "Done" -- Will always be executed
Next Generation Shell[edit]
- Defining custom exception type
- Raising exceptions
throw MyError("this happened")
- Exception handling and propagation
try { # something } catch(e:MyError) { guard e.val = 7 # ... } catch(e:MyError) { # ... } catch(e:Error) { # ... }
- Ignoring exceptions — try without catch
try 1/0 # evaluates to null
- Ignoring exceptions — «tor» operator
«tor» is try-or operator. In case of any exception when evaluating the argument on the left, evaluates to the argument on the right.
1/0 tor 20 # evaluates to 20
- «block» — facility to use exceptions to return a value
my_result = block my_block { # "block" catches exception thrown by return below # do calculation if calculation_finished() { my_block.return(42) # throws exception } }
Objective-C[edit]
- Exception declarations
NSException *exception = [NSException exceptionWithName:@"myException" reason:@"yourReason" userInfo:nil];
- Raising exceptions
- Exception handling and propagation
@try { ... } @catch (SomeException *se) { // Handle a specific exception type. ... } @catch (NSException *ne) { // Handle general exceptions. ... // Propagate the exception so that it's handled at a higher level. @throw; } @catch (id ue) { // Catch all thrown objects. ... } @finally { // Perform cleanup, whether an exception occurred or not. ... }
OCaml[edit]
Further information: OCaml
exception MyException of string * int (* exceptions can carry a value *) let _ = try raise (MyException ("not enough food", 2)); print_endline "Not reached" with | MyException (s, i) -> Printf.printf "MyException: %s, %dn" s i | e -> (* catch all exceptions *) Printf.eprintf "Unexpected exception : %s" (Printexc.to_string e); (*If using Ocaml >= 3.11, it is possible to also print a backtrace: *) Printexc.print_backtrace stderr; (* Needs to beforehand enable backtrace recording with Printexc.record_backtrace true or by setting the environment variable OCAMLRUNPARAM="b1"*)
Perl 5[edit]
Further information: Perl
The Perl mechanism for exception handling uses die
to throw an exception when wrapped inside an eval { ... };
block. After the eval
, the special variable $@
contains the value passed from die
.
Perl 5.005 added the ability to throw objects as well as strings. This allows better introspection and handling of types of exceptions.
eval { open(FILE, $file) || die MyException::File->new($!); while (<FILE>) { process_line($_); } close(FILE) || die MyException::File->new($!); }; if ($@) { # The exception object is in $@ if ($@->isa('MyException::File')) { # Handle file exception } else { # Generic exception handling # or re-throw with 'die $@' } }
The __DIE__
pseudo-signal can be trapped to handle calls to die
. This is not suitable for exception handling since it is global. However it can be used to convert string-based exceptions from third-party packages into objects.
local $SIG{__DIE__} = sub { my $err = shift; if ($err->isa('MyException')) { die $err; # re-throw } else { # Otherwise construct a MyException with $err as a string die MyException::Default->new($err); } };
The forms shown above can sometimes fail if the global variable $@
is changed between when the exception is thrown and when it is checked in the if ($@)
statement. This can happen in multi-threaded environments, or even in single-threaded environments when other code (typically
called in the destruction of some object) resets the global variable before the checking code.
The following example shows a way to avoid this problem (see [1][dead link] or [2]; cf. [3]). But at the cost of not being able to use return values:
eval { # Code that could throw an exception (using 'die') but does NOT use the return statement; 1; } or do { # Handle exception here. The exception string is in $@ };
Several modules in the Comprehensive Perl Archive Network (CPAN) expand on the basic mechanism:
Error
provides a set of exception classes and allows use of the try/throw/catch/finally syntax.TryCatch
,Try::Tiny
andNice::Try
all allow the use of try/catch/finally syntax instead of boilerplate to handle exceptions correctly.Exception::Class
is a base class and class-maker for derived exception classes. It provides a full structured stack trace in$@->trace
and$@->trace->as_string
.Fatal
overloads previously defined functions that return true/false e.g.,open
,close
,read
,write
, etc. This allows built-in functions and others to be used as if they threw exceptions.
PHP[edit]
Further information: PHP
// Exception handling is only available in PHP versions 5 and greater. try { // Code that might throw an exception throw new Exception('Invalid URL.'); } catch (FirstExceptionClass $exception) { // Code that handles this exception } catch (SecondExceptionClass $exception) { // Code that handles a different exception } finally { // Perform cleanup, whether an exception occurred or not. }
PowerBuilder[edit]
Exception handling is available in PowerBuilder versions 8.0 and above.
TRY // Normal execution path CATCH (ExampleException ee) // deal with the ExampleException FINALLY // This optional section is executed upon termination of any of the try or catch blocks above END TRY
PowerShell[edit]
Version 1.0[edit]
trap [Exception] { # Statements that execute in the event of an exception } # Statements in which exceptions might be thrown
Version 2.0[edit]
Try { Import-Module ActiveDirectory } Catch [Exception1] { # Statements that execute in the event of an exception, matching the exception } Catch [Exception2],[Exception3etc] { # Statements that execute in the event of an exception, matching any of the exceptions } Catch { # Statements that execute in the event of an exception, not handled more specifically }
Python[edit]
f = None try: f = open("aFileName", "w") f.write(could_make_error()) except IOError: print("Unable to open file") except: # catch all exceptions print("Unexpected error") else: # executed if no exceptions are raised print("File write completed successfully") finally: # clean-up actions, always executed if f: f.close()
R[edit]
tryCatch({ stop("Here an error is signaled") # default S3-class is simpleError a subclass of error cat("This and the following lines are not executed because the error is trapped beforen") stop( structure(simpleError("mySpecialError message"),class=c("specialError","error","condition")) ) } ,specialError=function(e){ cat("catches errors of class specialErrorn") } ,error=function(e){ cat("catches the default errorn") } ,finally={ cat("do some cleanup (e.g., setwd)n") } )
Rebol[edit]
Further information: Rebol
REBOL [ Title: "Exception and error handling examples" ] ; TRY a block; capturing an error! and converting to object! if error? exception: try [1 / 0][probe disarm exception] ; ATTEMPT results in the value of the block or the value none on error print attempt [divide 1 0] ; User generated exceptions can be any datatype! example: func ["A function to throw an exception" ][ throw "I'm a string! exception" ] catch [example] ; User generated exceptions can also be named, ; and functions can include additional run time attributes sophisticated: func ["A function to throw a named error exception" [catch] ][ throw/name make error! "I'm an error! exception" 'moniker ] catch/name [sophisticated] 'moniker
Rexx[edit]
Further information: Rexx
signal on halt; do a = 1 say a do 100000 /* a delay */ end end halt: say "The program was stopped by the user" exit
Ruby[edit]
begin # Do something nifty raise SomeError, "This is the error message!" # Uh-oh! rescue SomeError # This is executed when a SomeError exception # is raised rescue AnotherError => error # Here, the exception object is referenced from the # `error' variable rescue # This catches all exceptions derived from StandardError retry # This executes the begin section again else # This is executed only if no exceptions were raised ensure # This is always executed, exception or not end
S-Lang[edit]
try { % code that might throw an exception } catch SomeError: { % code that handles this exception } catch SomeOtherError: { % code that handles this exception } finally % optional block { % This code will always get executed }
New exceptions may be created using the new_exception
function, e.g.,
new_exception ("MyIOError", IOError, "My I/O Error");
will create an exception called MyIOError
as a subclass of IOError
. Exceptions may be generated using the throw statement, which can throw arbitrary S-Lang objects.
Smalltalk[edit]
[ "code that might throw an exception" ] on: ExceptionClass do: [:ex | "code that handles exception" ].
The general mechanism is provided by the message on:do:
.[6] Exceptions are just normal objects that subclass Error
, you throw one by creating an instance and sending it a #signal
message, e.g., MyException new signal
. The handling mechanism (#on:do:
) is again just a normal message implemented by BlockClosure
. The thrown exception is passed as a parameter to the handling block closure, and can be queried, as well as potentially sending #resume
to it, to allow execution flow to continue.
Swift[edit]
Exception handling is supported since Swift 2.
enum MyException : ErrorType { case Foo(String, Int) } func someFunc() throws { throw MyException.Foo("not enough food", 2) } do { try someFunc() print("Not reached") } catch MyException.Foo(let s, let i) { print("MyException: (s), (i)") } catch { print("Unexpected exception : (error)") }
Tcl[edit]
Further information: Tcl
if { [ catch { foo } err ] } { puts "Error: $err" }
Since Tcl 8.6, there is also a try command:
try { someCommandWithExceptions } on ok {res opt} { # handle normal case. } trap ListPattern1 {err opt} { # handle exceptions with an errorcode matching ListPattern1 } trap ListPattern2 {err opt} { # ... } on error {err opt} { # handle everything else. } finally { # run whatever commands must run after the try-block. }
VBScript[edit]
With New Try: On Error Resume Next 'do Something (only one statement recommended) .Catch: On Error GoTo 0: Select Case .Number Case 0 'this line is required when using 'Case Else' clause because of the lack of "Is" keyword in VBScript Case statement 'no exception Case SOME_ERRORNUMBER 'exception handling Case Else 'unknown exception End Select: End With ' *** Try Class *** Class Try Private mstrDescription Private mlngHelpContext Private mstrHelpFile Private mlngNumber Private mstrSource Public Sub Catch() mstrDescription = Err.Description mlngHelpContext = Err.HelpContext mstrHelpFile = Err.HelpFile mlngNumber = Err.Number mstrSource = Err.Source End Sub Public Property Get Source() Source = mstrSource End Property Public Property Get Number() Number = mlngNumber End Property Public Property Get HelpFile() HelpFile = mstrHelpFile End Property Public Property Get HelpContext() HelpContext = mlngHelpContext End Property Public Property Get Description() Description = mstrDescription End Property End Class
[7]
Visual Basic 6[edit]
Exception handling syntax is very similar to Basic. Error handling is local on each procedure.
On Error GoTo HandlerLabel 'When error has occurred jumps to HandlerLabel, which is defined anywhere within Function or Sub 'or On Error GoTo 0 'switch off error handling. Error causes fatal runtime error and stops application 'or On Error Resume Next 'Object Err is set, but execution continues on next command. You can still use Err object to check error state. '... Err.Raise 6 ' Generate an "Overflow" error using build-in object Err. If there is no error handler, calling procedure can catch exception by same syntax '... FinallyLabel: 'just common label within procedure (non official emulation of Finally section from other languages) 'cleanup code, always executed Exit Sub 'exits procedure 'because we are after Exit Sub statement, next code is hidden for non-error execution HandlerLabel: 'defines a common label, here used for exception handling. If Err.Number = 6 Then 'Select Case statement is typically better solution Resume FinallyLabel 'continue execution on specific label. Typically something with meaning of "Finally" in other languages 'or Resume Next 'continue execution on statement next to "Err.Raise 6" 'or Resume 'continue execution on (repeat) statement "Err.Raise 6" End If MsgBox Err.Number & " " & Err.Source & " " & Erl & " " & Err.Description & " " & Err.LastDllError 'show message box with important error properties 'Erl is VB6 build-in line number global variable (if used). Typically is used some kind of IDE Add-In, which labels every code line with number before compilation Resume FinallyLabel
Example of specific (non official) implementation of exception handling, which uses object of class «Try».
With New Try: On Error Resume Next 'Create new object of class "Try" and use it. Then set this object as default. Can be "Dim T As New Try: ... ... T.Catch 'do Something (only one statement recommended) .Catch: On Error GoTo 0: Select Case .Number 'Call Try.Catch() procedure. Then switch off error handling. Then use "switch-like" statement on result of Try.Number property (value of property Err.Number of build-in Err object) Case SOME_ERRORNUMBER 'exception handling Case Is <> 0 'When Err.Number is zero, no error has occurred 'unknown exception End Select: End With ' *** Try Class *** Private mstrDescription As String Private mlngHelpContext As Long Private mstrHelpFile As String Private mlngLastDllError As Long Private mlngNumber As Long Private mstrSource As String Public Sub Catch() mstrDescription = Err.Description mlngHelpContext = Err.HelpContext mstrHelpFile = Err.HelpFile mlngLastDllError = Err.LastDllError mlngNumber = Err.Number mstrSource = Err.Source End Sub Public Property Get Source() As String Source = mstrSource End Property Public Property Get Number() As Long Number = mlngNumber End Property Public Property Get LastDllError() As Long LastDllError = mlngLastDllError End Property Public Property Get HelpFile() As String HelpFile = mstrHelpFile End Property Public Property Get HelpContext() As Long HelpContext = mlngHelpContext End Property Public Property Get Description() As String Description = mstrDescription End Property
[7]
Visual Basic .NET[edit]
A Try
block must have at least one clause Catch
or Finally
clause and at most one Finally
clause.
Try ' code to be executed here Catch ex As Exception When condition ' Handle Exception when a specific condition is true. The exception object is stored in "ex". Catch ex As ExceptionType ' Handle Exception of a specified type (i.e. DivideByZeroException, OverflowException, etc.) Catch ex As Exception ' Handle Exception (catch all exceptions of a type not previously specified) Catch ' Handles anything that might be thrown, including non-CLR exceptions. Finally ' Always run when leaving the try block (including catch clauses), regardless of whether any exceptions were thrown or whether they were handled. ' Often used to clean up and close resources such a file handles. ' May not be run when Environment.FailFast() is called and in other system-wide exceptional conditions (e.g. power loss), or when the process crashes due to an exception in another thread. End Try
Visual Prolog[edit]
try % Block to protect catch TraceId do % Code to execute in the event of an exception; TraceId gives access to the exception information finally % Code will be executed regardles however the other parts behave end try
[8]
X++[edit]
public static void Main(Args _args) { try { // Code that could throw an exception. } catch (Exception::Error) // Or any other exception type. { // Process the error. } catch { // Process any other exception type not handled previously. } // Code here will execute as long as any exception is caught. }
References[edit]
- ^ Bjarne Stroustrup’s FAQ
- ^ «Handling exceptions». Archived from the original on 2014-01-02. Retrieved 2014-01-01.
- ^ «Exception handling tags». Archived from the original on 2014-01-02. Retrieved 2014-01-01.
- ^ https://issues.jboss.org/browse/RAILO-2176 # JBoss Community issue tracker ticket for adding
retry
- ^ Borland, Delphi Version 7.0, Online help
- ^ «Pharo by Example». Archived from the original on 2009-10-21. Retrieved 2010-03-20.
- ^ a b Try-Catch for VB
- ^ http://wiki.visual-prolog.com/index.php?title=Language_Reference/Terms#Try-catch-finally
See also[edit]
- Exception handling for the semantics of exception handling
- Syntax for definition of syntax in computer science
Написание надежного, без ошибок сценария bash всегда является сложной задачей. Даже если вы написать идеальный сценарий bash, он все равно может не сработать из-за внешних факторов, таких как некорректный ввод или проблемы с сетью.
В оболочке bash нет никакого механизма поглощения исключений, такого как конструкции try/catch. Некоторые ошибки bash могут быть молча проигнорированы, но могут иметь последствия в дальнейшем.
Проверка статуса завершения команды
Всегда рекомендуется проверять статус завершения команды, так как ненулевой статус выхода обычно указывает на ошибку
if ! command; then
echo "command returned an error"
fi
Другой (более компактный) способ инициировать обработку ошибок на основе статуса выхода — использовать OR:
<command_1> || <command_2>
С помощью оператора OR, <command_2> выполняется тогда и только тогда, когда <command_1> возвращает ненулевой статус выхода.
В качестве второй команды, можно использовать свою Bash функцию обработки ошибок
error_exit()
{
echo "Error: $1"
exit 1
}
bad-command || error_exit "Some error"
В Bash имеется встроенная переменная $?, которая сообщает вам статус выхода последней выполненной команды.
Когда вызывается функция bash, $? считывает статус выхода последней команды, вызванной внутри функции. Поскольку некоторые ненулевые коды выхода имеют специальные значения, вы можете обрабатывать их выборочно.
status=$?
case "$status" in
"1") echo "General error";;
"2") echo "Misuse of shell builtins";;
"126") echo "Command invoked cannot execute";;
"128") echo "Invalid argument";;
esac
Выход из сценария при ошибке в Bash
Когда возникает ошибка в сценарии bash, по умолчанию он выводит сообщение об ошибке в stderr, но продолжает выполнение в остальной части сценария. Даже если ввести неправильную команду, это не приведет к завершению работы сценария. Вы просто увидите ошибку «command not found».
Такое поведение оболочки по умолчанию может быть нежелательным для некоторых bash сценариев. Например, если скрипт содержит критический блок кода, в котором не допускаются ошибки, вы хотите, чтобы ваш скрипт немедленно завершал работу при возникновении любой ошибки внутри этого блока . Чтобы активировать это поведение «выход при ошибке» в bash, вы можете использовать команду set следующим образом.
set -e
# некоторый критический блок кода, где ошибка недопустима
set +e
Вызванная с опцией -e, команда set заставляет оболочку bash немедленно завершить работу, если любая последующая команда завершается с ненулевым статусом (вызванным состоянием ошибки). Опция +e возвращает оболочку в режим по умолчанию. set -e эквивалентна set -o errexit. Аналогично, set +e является сокращением команды set +o errexit.
set -e
true | false | true
echo "Это будет напечатано" # "false" внутри конвейера не обнаружено
Если необходимо, чтобы при любом сбое в работе конвейеров также завершался сценарий bash, необходимо добавить опцию -o pipefail.
set -o pipefail -e
true | false | true # "false" внутри конвейера определен правильно
echo "Это не будет напечатано"
Для «защиты» критический блока в сценарии от любого типов ошибок команд или ошибок конвейера, необходимо использовать следующую комбинацию команд set.
set -o pipefail -e
# некоторый критический блок кода, в котором не допускается ошибка или ошибка конвейера
set +o pipefail +e