return value of a function

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
      done
   g_RV=$files
   }
   
LAF "/tmp" 1; my_var1=$g_RV; echo "$my_var1";

#-- function calling other functions with g_RV
#---------------------------------------------
sum_A ()
   { 
   g_RV=$(($1+$2))
   }

sum_B()
   {
   g_RV=$(($1+$2))
   }

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

||

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.

Answered By: choroba
  • 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.
Answered By: Artem S. Tashkinov

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:

#!/bin/bash
funnystrings() {
    declare -n _dst="$1"
    _dst[0]="foo bar"
    _dst[1]="*"
    _dst[2]=$'newnline'
}

a=()
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
        _val=${src[$i]}
        _dst[$i]=$(( _val * _val ))
    done
}
a=(1 2 3)
b=()
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.

Answered By: ilkkachu

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.

Consider

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.

Answered By: Chris Davies
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.