Bash++ Return Stack
Tags: bash, linux, devops
Published: 2020-09-10 20:03:08

## Problem
To say that **Bash** has many idiosyncrasies is an understatement. One of the most troubling is the lack of an obvious way to return something besides an integer from a function without invoking the function in a subshell.
## Subshells
The term *subshell* means that **Bash** fork()'s a child process, and executes the function within that separate but initially identical process. If you want to write a multi-process application, this is a wonderful feature. If you simply want to get a complex return value from a function, this can be an exercise in masochism.
## Complex Return Values
Again: you can already return an integer from any **Bash** function without invoking a subshell; the problem is how to go about returning anything else.
One possible way to relay the result of a function is to store it in a global variable, and then retrieve it after the function returns. There isn't a lot of immediate appeal to this strategy since a single global variable dedicated for this purpose precludes the straightforward implementation of recursion, or having any function return more than one value.
## Return Stack
Stacks have been used in computing forever. Implementing one in **Bash** only takes 77 well-spaced lines. First off, there must be global array which can be used for this stack:
```
# Global array to use as a return stack
declare -g -a __RTN_STK
```
Notice the leading underscores to avoid namespace collisions. Next we'll need a way to push things onto this stack. Since **Bash** is casual about function prototypes, we'll leave it up to the caller to decide how many items to push on our stack:
```
function RTN_push
############################################################
# Use this to push things onto the global return stack
# Arguments:
#    Whatever strings you wish placed on the stack
#
{
   # Necessary to avoid "unbound variable" exception for empty stack
   local OPTS=$(shopt -o -p nounset errexit)
   set +ue

   # For readability, store array index value here before using
   local -i ndx

   # Push arguments onto the stack
   while [[ -n "$1" ]]; do

      # Array index is current size of array
      ndx=${#__RTN_STK[@]}

      # Place argument onto stack
      __RTN_STK[$ndx]="$1"

      # Discard argument from argv
      shift

   done

   # Restore options
   eval $OPTS
}
```
Excellent. Now we'll need a way to pop the values from the stack:
```
function RTN_pop
############################################################
# Use this to pop things off of the return stack
# Arguments:
#    Names of global varibles to be loaded by popping
#    strings from the stack.
#
{
   # Necessary to avoid "unbound variable" exception
   local OPTS=$(shopt -o -p nounset)
   set +u

   local -i arg ndx
   for (( arg= $#; arg ; --arg )); do
      ndx=${#__RTN_STK[@]}
      (( --ndx ))
      eval ${!arg}="\${__RTN_STK[\$ndx]}"
      # pop from stack, free memory
      unset __RTN_STK[$ndx]
   done

   # Restore options
   eval $OPTS
}

```
Finally, an example of how this may be used:
```
#!/bin/bash
############################################################
# Example script to demonstrate a function which returns
# multiple objects without the use of subshells
#
# John Robertson <john@rrci.com>
# Initial release: Thu Sep 10 11:58:23 EDT 2020
#

# Halt on error, no globbing, no unbound variables
set -efu

# import return stack tools
source ../bash++

function returns_3_strings ()
#######################################################
# Example function with 3 returns objects
# Arguments:
#   none
# Returns:
#  3 strings
#
{
   RTN_push 'string #1' 'string #2' 'string #3'
}

###################################
### Execution starts here #########
###################################

# NOTE: We'll reserve R1 R2 R3 ... global
# variables to fetch return values from
# return stack.

# Call our function
returns_3_strings

# Pop the results into global return "registers"
RTN_pop R1 R2 R3

# print the results
echo "R1= '$R1', R2= '$R2', R3= '$R3'"
```
... and the result:
```
R1= 'string #1', R2= 'string #2', R3= 'string #3'
```
Both files referenced in this article are available on [Github](https://github.com/jrbrtsn/bash_plus_plus)!