Join the Stack Overflow Community
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

Here's example that tries to execute command and checks if it was executed successfully, while capturing it's output for further processing:

#!/bin/bash

readonly OUTPUT=$(foo)
readonly RES=$?

if [[ ${RES} != 0 ]]
then
    echo "failed to execute foo"
    exit 1
else
    echo "foo success: '${OUTPUT}'"
fi

It reports that it was a success, even there is no such foo executable. But, if I remove readonly from OUTPUT variable, it preserves erroneous exit code and failure is detected.

I try to use readonly as for "defensive programming" technique as recommended somewhere... but looks like it bites itself in this case.

Is there some clean solution to preserve readonly while still capturing exit code of command/subshell? It would be disappointing that one has to remember this kind of exceptional use case, or revert to not using readonly ever...

Using Bash 4.2.37(1) on Debian Wheezy.

share|improve this question
    
Exactly the same issue with export by the way. – cdarke 11 hours ago
up vote 10 down vote accepted

The problem is that readonly is its own command and the exit code that it returns is its own exit code, not the exit code of the command substitution.

From help readonly:

Exit Status:
Returns success unless an invalid option is given or NAME is invalid.

So, you need to use two separate commands:

$ output=$(false)
$ readonly res=$?
$ readonly output

This saves the exit code that you want:

$ echo $res
1

Short of entering a debugger, there is no way to unset a readonly variable. So, don't set a variable readonly unless you want it to stay constant for the remainder of the bash session.

Variation

The two readonly commands can be combined into one (hat tip: Chepner):

$ output=$(false)
$ readonly output res=$?
$ echo $res
1

Aside

It is best practices to use lower- or mixed-case names for your variables. The system uses all upper-case names and you don't want to overwrite a system variable accidentally.

share|improve this answer
1  
To add to your aside on best practice, the { } around ${RES} are not performing any function. The [[ ]] are performing a textual comparison, to perform an arithmetic comparison use if (( RES != 0 )). – cdarke 11 hours ago
1  
Thanks, I didn't knew I can use readonly later. – Vincas Dargis 11 hours ago
    
You can also set the read-only flag on both variables with the same command: readonly res=$? output – chepner 6 hours ago
    
@chepner Very good. Answer updated to show a solution with a single readonly command. – John1024 11 mins 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.