Reputation: 1015
Code:
${non_existent_variable:+whatever} && echo "it works"
Result is it works
. But why? part before && evaluates to empty string. Trying to run however: && echo test
gives an error.
I've got this by accident trying to create ternary operator replacement for bash with different results for set and unset variable. It works this way, but I'm lost why it works at all.
PARAM=$(${tested_variable:+false} && echo var_exists || echo var_empty)
Upvotes: 4
Views: 151
Reputation:
${non_existent_variable:+whatever} && echo "it works"
Works (prints "it works) because[1] this is an AND and OR lists
:
AND and OR lists are sequences of one of more pipelines separated by the && and || control operators, respectively. AND and OR lists are executed with left associativity. An AND list has the form
command1 && command2
command2 is executed if, and only if, command1 returns an exit status of zero.
That is: anything as command1
that has an exit value of true will trigger the execution of the command2
after the &&
.
It is exactly the same kind of code as this:
true && echo "it works"
Which also is equivalent to this:
: && echo "it works"
And also works the same as (even if the variable is unset):
$ a=''
$ $a && echo "it works"
Yes, the variable expands to a null string
, but its exit status is zero:
From man bash
:
SIMPLE COMMAND EXPANSION
If there is a command name left after expansion, execution proceeds as described below. Otherwise, the command exits. If one of the expansions contained a command substitution, the exit status of the command is the exit status of the last command substitution performed. If there were no command substitutions, the command exits with a status of zero.
You can see the exit status with:
$ a=''
$ $a; echo "$?"
0
Even if there is a setting to false
before:
$ false; $a; echo "$?"
0
Also note that "$a"
will not work (and also will fail in all shells I tested):
$ "$a"
bash: : command not found
[1] Answered with bash syntax as this question was tagged bash. But what was explained above works in all shells I tested.
Upvotes: 1
Reputation: 530990
According to the section 2.9.1 of the POSIX spec:
A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.
The very last sentence of section 2.9.1 is the relavant one:
If there is a command name, execution shall continue as described in Command Search and Execution . If there is no command name, but the command contained a command substitution, the command shall complete with the exit status of the last command substitution performed. Otherwise, the command shall complete with a zero exit status.
So if after all expansion, redirections, etc, no command name is found, it is treated as completing successfully with an exit status of 0.
However, the grammar does not allow a null string prior to the &&
operator. The following is a syntax error:
&& echo it works
The only way to get a completely empty simple command is to have a non-empty string "evaporate" during expansion. One way of accomplishing that is with the unquoted parameter expansion that produces the empty string, which is exactly what ${non_existent_variable:+whatever}
does.
Upvotes: 6
Reputation: 42693
The resulting string is not important, what the &&
construct is testing is the exit code of the statement before it (i.e. $?
) and not what's echoed on screen. Because there wasn't an error in the parameter expansion (indeed, I'm not sure there could be an error) the statement continues.
In comments you asked why $(echo "") && echo test
echoes a value, and it's the same reason. The echo
statement exits 0
(i.e. true
) so the subshell exits 0
as well, and the statement continues running.
Upvotes: 2