views:

34

answers:

3

Are there any idioms for returning multiple values from a bash function within a script?

http://tldp.org/LDP/abs/html/assortedtips.html describes how to echo multiple values and process the results (e.g., example 35-17), but that gets tricky if some of the returned values are strings with spaces in.

A more structured way to return would be to assign to global variables, like

foo () {
    FOO_RV1="bob"
    FOO_RV2="bill"
}

foo
echo "foo returned ${FOO_RV1} and ${FOO_RV2}"

I realize that if I need re-entrancy in a shell script I'm probably doing it wrong, but I still feel very uncomfortable throwing global variables around just to hold return values.

Is there a better way? I would prefer portability, but it's probably not a real limitation if I have to specify #!/bin/bash.

A: 

Shell script functions can only return the exit status of last command executed or the exit status of that function specified explicitly by a return statement.

To return some string one way may be this:

function fun()
{
  echo "a+b"
}

var=`fun` # Invoke the function in a new child shell and capture the results
echo $var # use the stored result

This may reduce your discomfort although it adds the overhead of creation of a new shell and hence would be marginally slower.

Neeraj
A: 

you can make use of associative arrays with you have bash 4 eg

declare -A ARR
function foo(){
  ...
  ARR["foo_return_value_1"]="VAR1"
  ARR["foo_return_value_2"]="VAR2"
}

you can combine them as strings.

function foo(){
  ...
  echo "$var1|$var2|$var3"
}

then whenever you need to use those return values,

ret="$(foo)"
IFS="|"
set -- $ret
echo "var1 one is: $1"
echo "var2 one is: $2"
echo "var3 one is: $3"
ghostdog74
+1  A: 

Much as I love shell, it's probably the case that as soon as you're throwing arbitrary structured data around, Unix bourne/posix shell is not the right choice.

If there are characters which do not occur inside fields, then separate with one of those. The classic example is /etc/passwd, /etc/group and various other files which use a colon as a field separator.

If using a shell which can handle a NUL character inside strings, then joining on the NUL and separating on it (via $IFS or whatever) can work well. But several common shells, including bash, break on NUL. A test would be an old .sig of mine:

foo=$'a\0b'; [ ${#foo} -eq 3 ] && echo "$0 rocks"

Even if that would work for you, you've just reached one of the warning signs that it's time to switch to a more structured language (Python, Perl, Ruby, Lua, Javascript ... pick your preferred poison). Your code is likely to become hard to maintain; even if you can, there's a smaller pool of people who'll understand it well enough to maintain it.

Phil P