Problem
When I execute a variable assignment explicitly and with command substitution, I’m not sure what error code the command will return:
a=$(false); echo $?
It returns 1, which leads me to believe that variable assignment does not sweep or generate a new error code after the previous one. But here’s what happened when I tried it:
false; a=""; echo $?
It returns 0 because that’s what a=”” does, and it overrides the 1 provided by false.
I’m curious as to why this occurs; is there something unique about variable assignment that sets it apart from other commands? Or is it just because a=$(false) is regarded as a single command, and only the command replacement part makes sense?
— UPDATE —
Thanks to everyone who responded; I understood “When you assign a variable using command replacement, the exit status is the command’s status” from the responses and comments. (by @Barmar), this explanation is excellently clear and easy to comprehend, but the language isn’t precise enough for programmers; I’d want to see a reference to this point from authority such as the TLDP or the GNU man page; please assist me in finding it; thank you!
Asked by Reorx
Solution #1
When you run a command as $(cmd), the command’s output will replace itself.
When you say:
a=$(false) # false fails; the output of false is stored in the variable a
The variable an is used to store the output of the command false. Furthermore, the exit code is the same as the command’s output. False assistance might state:
false: false
Return an unsuccessful result.
Exit Status:
Always fails.
Saying, on the other hand:
$ false # Exit code: 1
$ a="" # Exit code: 0
$ echo $? # Prints 0
causes the assignment to a to return the exit code 0, which is 0.
EDIT:
The following is a quote from the manual:
Quoting from BASHFAQ/002:
Answered by devnull
Solution #2
Note that when paired with local, as in local variable=”$(command),” this isn’t the case. Even if the command fails, the form will leave successfully.
Consider the following Bash script:
#!/bin/bash
function funWithLocalAndAssignmentTogether() {
local output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
function funWithLocalAndAssignmentSeparate() {
local output
output="$(echo "Doing some stuff.";exit 1)"
local exitCode=$?
echo "output: $output"
echo "exitCode: $exitCode"
}
funWithLocalAndAssignmentTogether
funWithLocalAndAssignmentSeparate
Here’s what you’ll get as a result of this:
nick.parry@nparry-laptop1:~$ ./tmp.sh
output: Doing some stuff.
exitCode: 0
output: Doing some stuff.
exitCode: 1
This is due to the fact that local is a builtin command, and a command like local variable=”$(command)” calls local after substituting command’s output. As a result, you obtain the exit status from local.
Answered by Nick P.
Solution #3
Yesterday, I ran through the same issue (Aug 29 2018).
Declare in global scope has the same behaviour as local, as noted in Nick P.’s answer and @sevko’s comment in the accepted answer.
My Bash code is as follows:
#!/bin/bash
func1()
{
ls file_not_existed
local local_ret1=$?
echo "local_ret1=$local_ret1"
local local_var2=$(ls file_not_existed)
local local_ret2=$?
echo "local_ret2=$local_ret2"
local local_var3
local_var3=$(ls file_not_existed)
local local_ret3=$?
echo "local_ret3=$local_ret3"
}
func1
ls file_not_existed
global_ret1=$?
echo "global_ret1=$global_ret1"
declare global_var2=$(ls file_not_existed)
global_ret2=$?
echo "global_ret2=$global_ret2"
declare global_var3
global_var3=$(ls file_not_existed)
global_ret3=$?
echo "global_ret3=$global_ret3"
The output:
$ ./declare_local_command_substitution.sh 2>/dev/null
local_ret1=2
local_ret2=0
local_ret3=2
global_ret1=2
global_ret2=0
global_ret3=2
In the output above, take note of the values of local ret2 and global ret2. The local and declare commands overwrite the exit codes.
My Bash version:
$ echo $BASH_VERSION
4.4.19(1)-release
Answered by Zhi Zhu
Solution #4
(Not a response to the original question, but it’s too long to comment on)
It’s worth noting that export A=$(false); echo $? returns 0! The rules mentioned in devnull’s response don’t seem to apply any longer. To put the quote in context (emphasis mine), consider the following:
According to the instructions, var=foo is a particular instance of the var=foo command… syntax (which is a bit confusing!). Only the no-command case is covered by the “exit status of the last command substitution” rule.
While it’s tempting to think of export var=foo as “modified assignment syntax,” it’s not — it’s a built-in command (that just happens to take assignment-like args).
=> Exporting a var AND capturing command substitution status should be done in two stages:
A=$(false)
# ... check $?
export A
This method also works in set -e mode, exiting immediately if the command replacement returns a value other than zero.
Answered by Beni Cherniavsky-Paskin
Post is based on https://stackoverflow.com/questions/20157938/exit-code-of-variable-assignment-to-command-substitution-in-bash