Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

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

See the following examples and their outputs in POSIX shells:

  1. false;echo $? or false || echo 1: 1
  2. false;foo="bar";echo $? or foo="bar" && echo 0: 0
  3. foo=$(false);echo $? or foo=$(false) || echo 1: 1
  4. foo=$(true);echo $? or foo=$(true) && echo 0: 0

As mentioned by the highest-voted answer at https://stackoverflow.com/questions/6834487/what-is-the-variable-in-shell-scripting:

$? is used to find the return value of the last executed command.

This is probably a bit misleading in this case, so let's get the POSIX definition which is also quoted in a post from that thread:

? Expands to the decimal exit status of the most recent pipeline (see Pipelines).

So it appears as if a assignment itself counts as a command (or rather a pipeline part) with a zero exit value but which applies before the right side of the assignment (e.g. the command substitution calls in my examples here).

I see how this behavior makes sense from a practical standpoint but it seems somewhat unusual to me that the assignment itself would count in that order. Maybe to make more clear why it's strange to me, let's assume the assignment was a function:

ASSIGNMENT( VARIABLE, VALUE )

then foo="bar" would be

ASSIGNMENT( "foo", "bar" )

and foo=$(false) would be something like

ASSIGNMENT( "foo", EXECUTE( "false" ) )

which would mean that EXECUTE runs first and only afterwards ASSIGNMENT is run but it's still the EXECUTE status that matters here.

Am I correct in my assessment or am I misunderstanding/missing something? Are those the right reasons for me viewing this behavior as "strange"?

share|improve this question
1  
Sorry, but it's unclear to me what you find strange. – Kusalananda 18 hours ago
1  
@Kusalananda Maybe it helps to tell you that it started with me asking myself: "Why does false;foo="bar";echo $? always return 0 when the last real command that ran was false?" It's basically that assignments behave special when it comes to exit codes. Their exit code is always 0, except when it isn't because of something which ran as part of the right-hand side of the assignment. – phk 18 hours ago
1  

The exit status for assignments is strange. The most obvious way for an assignment to fail is if the target variable is marked readonly.

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

Note that neither the true nor false paths of the if statement were taken, the assignment failing stopped execution of the entire statement. bash in posix mode and ksh93 and zsh will all abort a script if an assignment fails.

To quote the posix standard "A command without a command name, but one that includes a command substitution, has an exit status of the last command substitution that the shell performed.". This is exactly the part of the shell grammar involved in

 foo=$(err 42)

which comes from a simple_command (simple_command -> cmd_prefix -> ASSIGNMENT_WORD). So if an assignment succeeds then the exit status is zero unless command substitution was involved, in which case the exit status is the status of the last one. If the assignment fails then the exit status is non-zero, but you may not be able to catch it.

share|improve this answer

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.