Can somebody explain me, why the return value of a bash function is always echoed in the function and then consumed by my_var=$(my_func arg1 arg2 ..), when i look through code examples.

my_func ()
   echo "$1 , $2, $3"

my_var=$(my_func .. .. ..);

instead of using this, which would not open a subshell

declare g_RV  #-- global return value for all functions

myfunc ()
   g_RV="$1 , $2, $3"

myfunc .. .. ..; my_var=$g_RV;

because i use the second way, and wonder, if this would fail against the first method in some cases. There must be a cause, because everyone is opening a subshell.

EDIT: because of Kusalananda and Paul_Pedant comments
I added a recursive function call with g_RV - LAF function , list all files of a directory $1, or do it recursive if $2 INT>0
and then function calls inside a function to other functions (look at sum functions!

declare g_RV

shopt -s dotglob nullglob

#-- Recursive call with g_RV
#-- call: LAF [directory STR] [recursive INT (>0 .. true, 0 .. false)]  ... List all files in a folder or inclusive all files in subfolders (recursive
LAF ()
   local file files="" dir_path=$1
   if [[ ${dir_path:0:1} != "/" ]]; then dir_path=$(readlink -f "$dir_path"); fi
   for file in "$dir_path/"*; do
      if [[ -d $file ]]; then (($2)) && { LAF "$file"; files+=$g_RV; }                    #-- recursive call
      else files+="${file}"$'\n'; fi
LAF "/tmp" 1; my_var1=$g_RV; echo "$my_var1";

#-- function calling other functions with g_RV
sum_A ()


sum_C ()
   local -i sum=0;
   sum_A 5 6; sum+=$g_RV
   sum_B 12 18; sum+=$g_RV
sum_C; my_var2=$g_RV; echo "sum: $my_var2";

4 Answers 4


Standard tools and utilities return information via stdout, qualifying the result with the exit status (0=ok, otherwise error). Your functions should do the same so that they can be used in the same consistent manner.

Your example shows a single global variable for all function returns. As soon as you take this approach you cannot interpolate functions in pipelines or even in strings without increasing complexity and reducing readability.


f() { sed 's/^/> /'; }
g() { nl; }

echo try | f | g    # "     1  > try"

If you have a global variable for each function's return (or worse, the same global variable for both) you would need to jump through hoops to achieve the same effect.

You're running a shell, and by the time you consider all the overhead the number of subprocesses is - or should be - largely irrelevant. If you need speed don't write in shell script.


Using a global variable is not reusable in general. There can be a name clash with a global variable used somewhere else.

Also, it's not the standard way how most people expect functions to work.

  • Less code
  • Easier to read
  • The traditional way is less likely to lead to mistakes
  • echo is a built-in function which computationally costs almost nothing
  • Bash is a Bourne Again SHell, it is not a [classic] programming language
  • The classic way of doing things is resilient against source/. - meaning it will always work regardless.
  • it's not echo that adds the cost, but the subshell (due to forking a new process, in shells that are not ksh, that is)
    – ilkkachu
    Mar 13 at 10:08

It's not always done like that. In fact, you just did it in another way in your very question!

Shell functions can't actually return anything other than an exit status, which is limited to an integer between 0..255, so not very useful. Then, the options are to either use an external variable, or to print the required data and capture the output.

Printing and capturing the output isn't without issues either; even if we ignore the performance issues that most shells suffer with command substitutions, there's also the issue that the output is a single string or stream of bytes, devoid of any structure. And in shells other than zsh, a command substitution can't capture NUL bytes anyway, so it's not even an 8-bit clean stream of bytes.

If you need to e.g. return an array, using an external variable may be much simpler. Instead of using one with a fixed name, you might use namerefs to allow the caller to pass the name of the variable to use for the returned values.

E.g. as a stupid example, just to have something with values that are awkward to handle without proper arrays:

funnystrings() {
    declare -n _dst="$1"
    _dst[0]="foo bar"

funnystrings a
declare -p a

Similarly, you could pass another variable name if you needed in input variable:

squares() {
    declare -n _src="$1" _dst="$2"
    local _i _val
    for _i in "${!_src[@]}"; do
        _dst[$i]=$(( _val * _val ))
a=(1 2 3)
squares a b

That actually might work in Bash even if a and b were local variables in another function that called squares a b, but note that there may be oddities in how nested locals work between shells, esp. for what goes with unset.

  • @ikkachu when you look at your squares function, that would lead to problems if $1 is "val", if the user reference a variable name you use locally, than the local variable src is linked to the local variable val, instead to the global variable val, and you never new which variable name the user wants to use, it can hit your local variable. so when i do a local -n in a function, i always use a prefix "_" for each variable in the function, and tell the user not to use a variable name with prefix _, if he wants to call this function. I already know this. thx Mar 13 at 11:53
  • @Schmaehgrunza, yes, and i is also a rather likely variable name (though maybe not for an array). But yes, that's the issue with namerefs, since they're not actual references in Bash, just names.
    – ilkkachu
    Mar 13 at 12:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .