Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

In my project I have the following snippet:

local output="$(bash "${1##*/}")"
echo "$?"

This always prints zero due to local, however, removing local causes the $? variable to behave correctly: which is to assume the exit code from the subshell.

My question is: how I can keep this variable local whilst also capturing the exit value?

share|improve this question
    
shellcheck will not only catch this issue but suggest the solution at unix.stackexchange.com/a/281749/24718! – Waleed Khan 6 hours ago
up vote 8 down vote accepted
#!/bin/bash
thing() {
   local FOO=$(asjkdh) RET=$?
   echo $RET
}

This will echo 127, the correct error code for "command not found".

You can use local to define more than one variable. So I just also create the local variable RET to capture the exit code of the subshell before local succeeds and sets $? to zero.

share|improve this answer
    
Is there any more information on this syntax? I haven't seen it before. – Bourgond Aries 13 hours ago
    
You can use local to define more than one variable. So I just also create the local variable RET to capture the exit code of the subshell before local succeeds and sets $? to zero. – DopeGhoti 13 hours ago
    
Ah, that makes a tonne of sense! I suggest you add it to the answer (then I'll upvote it too). – Bourgond Aries 13 hours ago
4  
As an aside, using all-caps variable names is bad form. See the POSIX environment variable spec at pubs.opengroup.org/onlinepubs/009695399/basedefs/…, which describes all-upper-case names as reserved for variables with meaning to the shell or system and names with at least one lower-case character reserved for application-defined use, keeping in mind that shell variables and environment variables share a namespace (since, in the event of collisions, assigning to the former can override the latter). – Charles Duffy 10 hours ago
1  
@fpmurphy1, using all-caps variable names in scripts that are not intended to be environment variables (i.e. that you do not intend to export or expect to have been exported from a parent shell) is a bad idea, even though it is technically allowed by POSIX. You also have the technical ability to hardcode library paths, the ability to silently use a default instead of failing when given unexpected arguments, and the ability to print to /dev/tty instead of stdout. These are also generally very bad ideas. The fact that something is possible is not a good defense for its advisability. – Wildcard 4 hours ago

Declare the local variable before you assign to it:

thing() {
  local output
  output="$(bash "${1##*/}")"
  echo "$?"
}

In my opinion this is also more readable than setting an additional RET variable. YMMV on that, but it works just as you would expect.

share|improve this answer
    
This is much better than using a separate variable, as should be obvious if you want to check the return code of multiple assignments: simply local var1 var2 ... and Bob's your uncle. – l0b0 1 hour ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.