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.
The values of variables with the readonly attribute cannot be changed by subsequent assignment, nor can those variables be unset by the unset utility. If the name of a variable is followed by = word, then the value of that variable shall be set to word.
Read-only VariablesShell provides a way to mark variables as read-only by using the read-only command. After a variable is marked read-only, its value cannot be changed. For example, the following script generates an error while trying to change the value of NAME − #!/bin/sh NAME="Zara Ali" readonly NAME NAME="Qadiri"
The shell provides a way to protect variables as read-only by using the readonly command. After a variable is marked read-only, we cannot change the value of the variable. As we can see from the output, we cannot assign to the variable declared as readonly. If we drop "$", we get an error.
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.
The two readonly
commands can be combined into one (hat tip: Chepner):
$ output=$(false)
$ readonly output res=$?
$ echo $res
1
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With