11

I've found the strange behaviour for me, which I can't explain. The following code is work OK:

function prepare-archive {
blah-blah-blah...
_SPEC_FILE=$(check-spec-file "$_GIT_DIR/packaging/")
exit $?
blah-blah-blah...
}

means I get value which I expect:

bash -x ./this-script.sh:
++ exit 1
+ _SPEC_FILE='/home/likern/Print/Oleg/print-service/packaging/print-service.spec
/home/likern/Print/Oleg/print-service/packaging/print-service2.spec'
+ exit 1

As soon as I add local definition to variable:

local _SPEC_FILE=$(check-spec-file "$_GIT_DIR/packaging/")

I get following:

bash -x ./this-script.sh:
++ exit 1
+ local '_SPEC_FILE=/home/likern/Print/Oleg/print-service/packaging/print-service.spec
/home/likern/Print/Oleg/print-service/packaging/print-service2.spec'
+ exit 0
$:~/MyScripts$ echo $?
0

Question: Why? What has happened? Can I catch output from subshell to local variable and check subshell's return value reliably?

P.S.: prepare-archive is called in the main shell script. The first exit is the exit from check-spec-file function, the second from prepare-archive function - this function itself is executed from main shell script. I return value from check-spec-file by exit 1, then pass this value to exit $?. Thus I expect they should be the same.

1
  • In what context is prepare-archive called? The ++ exit 1 doesn't fit with any code you've shown. Commented Oct 11, 2012 at 13:35

3 Answers 3

24

To capture subshell's exit status, declare the variable as local before the assignment, for example, the following script

#!/bin/sh

local_test()
{
    local local_var
    local_var=$(echo "hello from subshell"; exit 1)
    echo "subshell exited with $?"
    echo "local_var=$local_var"
}

echo "before invocation local_var=$local_var in global scope"
local_test
echo "after invocation local_var=$local_var in global scope"

produces the following output

before invocation local_var= in global scope
subshell exited with 1
local_var=hello from subshell
after invocation local_var= in global scope
Sign up to request clarification or add additional context in comments.

3 Comments

Would you mind explaining what's happening here? Your answer fixed my problem, but I still don't understand how and why it does so.
@IhorKaharlichenko If a variable is assigned on declaration with local, sub-shell exit status is "masked"/overriden by the exit status of the local built-in command. Declaring a local variable before assignment is also more portable (some shells don't support initialization with local).
7

From the bash manual, Shell Builtin Commands section:

local:
    [...]The return status is zero unless local is used outside a function, an invalid name is supplied, or name is a readonly variable. 

Hope this helps =)

5 Comments

Does this mean that variable definition doesn't have return status when is used without local? Why? Do you know some workarounds - as I don't want produce superfluous global variables.
I'm not aware of any workarounds that don't involve the creation of a global variable or the use of files, sorry =(. I would do local TMPFILE=$(mktemp); check-spec-file > $TMPFILE; local error=$?; local _SPEC_FILE=$(cat $TMPFILE); rm $TMPFILE
@Mephi_stofel - to work around it, you can split the declaration and initialization, e.g. first this: local my_var and than that: my_var=$(my_function). the initialization will safely refer to the local variable, without polluting the global scope.
Thank you, @EliranMalka! I had the same problem today and this is exactly how I solved it.
Amazing to find this question after you debug for one hour and only then you know how to search on Google/SO. ;)
0

As I use bash subshell parenthesis to group many echo commands I hit this strange problem. In my case all I needed was to pass one value back to the calling shell so I just used the exit command

RET=0
echo RET: $RET
(echo hello
echo there
RET=123
echo RET: $RET
exit $RET)
RET=$?
echo RET: $RET

gives the following output

RET: 0
hello
there
RET: 123
RET: 123

without the exit command you will get this which is confusing:

RET: 0
hello
there
RET: 123
RET: 0

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.