1

In a bash function, I'm trying to capture the output of a command along with it's exit code. Generally, my form is

some_function() {
local rv=$(some_other_function)
local code=$?
echo "$rv"
return $code
}

but I notice that whenever I use 'local', then $? is ALWAYS 0. Like it's capturing the result of the assignment, not the called function. If I don't use 'local', then it works as expected:

$ foo() { local rv=$(false); echo "code is $?"; }
$ foo
code is 0
$ foo() { rv=$(false); echo "code is $?"; }
$ foo
code is 1

Can someone explain this to me? Obviously something fundamental here I just don't understand.

  • There's another problem with `local rv=$(some_other_function)` -- in some shells (not including bash), the output from `some_other_function` will be word-split before being passed to `local`; the first word will be assigned to `rv`, and the rest will be treated as separate identifiers to be marked as local variables (except for the ones that aren't valid identifiers). It's safest to just always double-quote command substitutions, e.g. `local rv="$(some_other_function)"`, just like you should variable references. – Gordon Davisson Mar 03 '21 at 03:37

1 Answers1

5

Can someone explain this to me?

In simple words most of the time $? has the exit status of last command executed. The last command is local. The return status of local is zero - it successfully made rv a local variable. Stupid example:

echo $(false) $(true) $(false)
^^^^ - this is the last command executed
# $? here has exit status of **echo**

Separate the assignment from local:

local rv code
# Assignment is not a "command", in the sense it preserves exit status.
# The last command executed in assignment is the one inside `$(...)`.
rv=$(some_other_function)
code=$?  # will have the exit status of expression executed inside $(...)

Check your scripts with http://shellcheck.net . Shellcheck warns about such mistakes.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111