Assigning exit code to a shell local variable

132,427

Solution 1

local t1=$(exit 1) tells the shell to:

  • run exit 1 in a subshell;
  • store its output (as in, the text it outputs to standard output) in a variable t1, local to the function.

It's thus normal that t1 ends up being empty.

($() is known as command substitution.)

The exit code is always assigned to $?, so you can do

function0()
{
  (exit 1)
  echo "$?"
}

to get the effect you're looking for. You can of course assign $? to another variable:

function0()
{
  (exit 1)
  local t1=$?
  echo "$t1"
}

Solution 2

Exit code was stored in $? variable. Using Command Substitution only capture the output, you should use (...) to create subshell:

#!/bin/bash

func() {
  (exit 1)
  local t1=$?
  printf '%d\n' "$t1"
}

func

Solution 3

In bash this works:

loc(){  local   "x=$(exit "$1"):$?"
        printf  '$%s:\t%d\n' \
                 x "${x##*:}" \? "$?"
}

It has to do with the order of command evaluation and variable assignment. local has a return value all its own - and it is the currently executing command, not the command substitution. The reason things like...

x=$(exit 1); echo "$?"

...can return 1 is because there never is a return in that command except for the subshell run to assign $x's value - so $? doesn't get clobbered as it does in practically every other case in which command substitutions are used.

Anyway, with local it does get clobbered - but if you catch it at just the right time - which is while the expansions are still being evaluated and before local's routines have a chance to clobber it - you can still assign it.

unset x; loc 130; echo "${x-\$x is unset}"

...prints...

$x: 130
$?: 0
$x is unset

You should know though that in many shells you cannot rely upon $? being set mid-evaluation in that way. In fact, that's probably because those shells do not bother re-evaluating at every possible juncture as perhaps bash does - which I would argue is probably better behavior than bash's. Do you really want your interpreter recursively loop-evaluating values which are very likely to be overwritten before ever you have the chance to use them?

Anyway, that's how you can do that.

Solution 4

This does not answer this question exactly, but I think in majority of cases you would make decisions based on the acquired exit code later down the execution stream. If you need to check for success/failure exit code only – it can easily be achieved without storing the code into a variable:

if (exit 1); then
  echo "Command succeeded, alles gut"
else
  echo "Oi oi, Huston, we got alles kaputt 'round here"
fi

Solution 5

@mikeserv's answer is great, and provides explanation for behavior I had discovered but had no idea why it was happening.

I just wanted to add an easy way to avoid local eating the exit code of a subshell is to do:

val_check_exit() {
   local val=
   val=$(echo "hi"; exit 1)
   local res=$?
   echo "val is ${val}, exit code ${res}"
}
val_check_exit
# outputs:
# val is hi, exit code 1
Share:
132,427

Related videos on Youtube

Gilles 'SO- stop being evil'
Author by

Gilles 'SO- stop being evil'

Updated on September 18, 2022

Comments

  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' almost 2 years
    #!/bin/bash
    function0()
    {
     local t1=$(exit 1)
     echo $t1
    }
    
    function0
    

    echo prints empty value. I expected:

    1
    

    Why doesn't t1 variable get assigned the exit command's return value - 1?

  • cuonglm
    cuonglm about 9 years
    @Dani_l: Thanks, that's a mis-typo. Updated.
  • mikeserv
    mikeserv about 9 years
    You know, you can always put the return into the pipe, too. `$(trap 'printf "::ERRNO:$?"' 0; # now do whatever however - that trap will ensure the last string written is the last return for the whole substitution context.
  • phyatt
    phyatt over 7 years
    Note that Command Substitution only captures standard out unless redirected differently.
  • Nick Matteo
    Nick Matteo over 5 years
    All the claimed outputs are wrong, due to the fact that command substitution doesn't give the exit code, which is the whole point of the question. It's not clear what the "dirty trick" has to do with anything.
  • Cheetaiean
    Cheetaiean over 4 years
    How about if this was outside a function? I am running bash code in a Makefile and cannot use local. Without local the same problem persists as in the original question.
  • Stephen Kitt
    Stephen Kitt over 4 years
    @Cheetaiean please ask a new question with details of what you’re trying to do and the problems you’re having.
  • spawn
    spawn over 3 years
    Going a step further: I found this question because I wanted to capture the output of a command into a local variable and use the exit code afterwards. This can be done by simply declaring it first: function0(){ local foo; foo=$(echo bar; exit 1); echo "returned $?"; }