Tuesday, September 27, 2011

Bash Function Return Values and Exit

For those lucky souls out there programming in bash, you may be making use of the bash subshell trick that allows you to "return" a value from a function.

function echoTwice() {
  echo "$1"
  echo "$1"
}

declare twice=$(echoTwice "woot")
echo "Twice is: $twice"

Will produce the output,

Twice is: woot woot

The tricky gotcha in this situation involves exiting on error conditions. For example, consider the following enhancement to echoTwice,

function echoTwice() {
  if [ -z "$1" ]; then
    echo "Must provide an argument" >&2
    exit 1
  fi

  echo "$1"
  echo "$1"
}

declare twice=$(echoTwice)
echo "Twice is: $twice"

You would expect to see the error message and nothing else.  However, instead you see

Must provide an argument
Twice is:

Why does this happen? It happens because the assignment to $twice happens via a subshell. A subshell is spawned to evaluate the result of echoTwice. When that subshell exits early, bash treats it no differently than had the whole function proceded. Subsequently, rather than quitting your code, processing continues when the subshell completes. So, how do you accomodate this in your code without resorting to ugly globals?

The solution that I have found is to test if $twice was assigned.

if [ -z "$twice" ]; then
  # Error message emitted by echoTwice prints to STD_ERR and
  # ...will not be consumed by the sub-shell
  exit 1
fi

The reason I can't just check the exit status is because declare twice= is an actual execution and will typically have a valid exit status.

EDIT (Nov 2nd, 11): It turns out that you can check the return status! The issue is accurately described that declare twice= has its own exit status, but the exit status arises from the use of the bash built-in "declare" and NOT from the assignment to the variable (See the "declare" section of http://www.gnu.org/software/bash/manual/bashref.html#Bash-Builtins).  So, the solution becomes:

declare twice=
twice=$(echoTwice)
if [ $? -ne 0 ]; then
  # Error message emitted by echoTwice prints to STD_ERR and
  # ...will not be consumed by the sub-shell
  exit 1
fi
echo "Twice is: $twice"

No comments:

Post a Comment