mogli
mogli

Reputation: 1609

exit code of function call ignored if stored in local variable

Below is the sample scenario :-

There is a sample function defined that is echoing some text, as well as setting some exit code using return. There is another script that is calling this function. Below is the simplified code :-

~/playground/octagon/bucket/test/sample $

pwd
/Users/mogli/playground/octagon/bucket/test/sample

~/playground/octagon/bucket/test/sample $

cat functions.sh
myfunc() {
    echo "This is output $1"
    return 3
}

~/playground/octagon/bucket/test/sample $

cat example.sh
. functions.sh

function example(){
    myfunc_out=$(myfunc $1); myfunc_rc=$?
    echo "myfunc_out is: $myfunc_out"
    echo "myfunc_rc is: $myfunc_rc"
}

example $1

~/playground/octagon/bucket/test/sample $

sh example.sh 44
myfunc_out is: This is output 44
myfunc_rc is: 3

Now, if I use local for variables that are used to store function return value and exit code in example.sh, then exit code is not coming correctly. Please find below modified example.sh :-

~/playground/octagon/bucket/test/sample $

cat example.sh
. functions.sh

function example(){
    local myfunc_out=$(myfunc $1); local myfunc_rc=$?
    echo "myfunc_out is: $myfunc_out"
    echo "myfunc_rc is: $myfunc_rc"
}

example $1

~/playground/octagon/bucket/test/sample $

sh example.sh 44
myfunc_out is: This is output 44
myfunc_rc is: 0

Upvotes: 2

Views: 225

Answers (1)

William Pursell
William Pursell

Reputation: 212664

When you write:

var=$(cmd)

the output of cmd is assigned to var (without word splitting) and $? is set to the value returned by cmd.

When you write:

var=$(cmd1) cmd2

cmd1 is executed and its output (without word splitting) is assigned to the variable var in the environment of cmd2, which is then executed. $? is set to the value returned by cmd2.

When you write:

cmd1 var=$(cmd2)

cmd2 is executed, the string var=output of cmd2 is subject to word splitting and passed as argument(s) to cmd1, and $? is set to the value returned by cmd1. (In almost all cases you want to supress the word splitting and instead write cmd1 var="$(cmd2)", which will guarantee that only one argument is passed.)

local is a command, and local myfunc_out=$(myfunc $1) is of the 3rd form (with a caveat), so it sets $? to the value returned by local. Note that if the output of myfunc $1 contains whitespace, word splitting does not take place. To quote the manpage: Assignment statements may also appear as arguments to the alias, declare, typeset, export, readonly, and local builtin commands, so the string counts as a variable assignment and word splitting is not done.

In short, local has an exit value, and it is being used to set $?

Instead of making the assignment an argument to local, you could use:

local myfunc_out myfunc_rc
myfunc_out="$(myfunc $1)"; myfunc_rc=$?

Note that the double quotes are not strictly necessary here, as word splitting does not take place in an assignment, but using them is definitely good practice.

Upvotes: 4

Related Questions