Functions: Exceptions

Exceptions in any language can be very useful to get out of deeply nested function calls, without having to pedantically check return values. If exceptions are used only in truly exceptional cases, the omission of the return value tests improves readability and can bring a nice speed up.

You can get exception-like functionality in a shell script with the use of subshells and exit. Usually exit terminates your shell script, but in a subshell it terminates just your subshell.

Note

Some time after writing this chapter, I chanced upon the bash-oo-framework, which takes this idea a lot further. With a “real” try/catch mechanism. Check it out.

deep()
{
   exit 22
}

foo()
{
   local rc

   (
      deep
   )
   rc=$?

   if [ $rc -ne 0 ]
   then
      printf "%s\n" "deep failed ($rc)" >&2
      return 1
   fi
   return 0
}

foo
deep failed (22)

There is no way we can pass a global variable out of a subshell. So we have to live with the eight bit return status and standard I/O.

Subshell functions

You can declare a function to be always running as a subshell, by replacing the curly braces with parentheses, as shown here with deep:

deep()
(
   exit 22
)

foo()
{
   local rc

   deep
   rc=$?

   if [ $rc -ne 0 ]
   then
      printf "%s\n" "deep failed ($rc)" >&2
      return 1
   fi
   return 0
}

foo

Subshell functions are somewhat fascinating, but I don’t find them really useful, preferring a subshell enclosure around the function call in the caller. This gives me more control.

Note

I have no naming scheme for functions signalling an exception. Probably, because I was burned by Java :)