Can we use bash's conditional operator with assignment operators after colon?
Bash reference manual explains the arithmetic operators as follows.
- conditional operator
expr ? expr : expr
- assignment
= *= /= %= += -= <<= >>= &= ^= |=
First, this code seems to work well:
a=1; ((a? b=1 : 2 )) #seems to work
But when I use assignment operators after :
, I got 'attempted assignment to non-variable' error:
a=1; ((a? b=1 : c=1)) #attempted assignment to non-variable error
Why can we use only assignment operators before colon?
3 Answers 3
Bash parses your last command as
a=1; (( (a? b=1 : c)=1 ))
which should make clear why it does not work. Instead you have to use
a=1; (( a? b=1 : (c=1) ))
This is called a ternary assignment
expression. Here's a bit from another answer of mine:
% a=abc
% b=abcd
% t=10 ; f=5
% echo $((r=${#a}>${#b}?t:f)) ; echo $r
> 5
> 5
% echo $((r=${#a}<${#b}?t:f)) ; echo $r
> 10
> 10
You see, as you say, this is a conditional assignment operation. It depends upon conditions. The syntax works like this:
$((var = $condition <compare_operator> $condition \
?if $true_assign :or $false_assign ))
I don't believe you are using this correctly.
From wikipedia:
?:
is used as follows:
condition ? value_if_true : value_if_false
The condition is evaluated true or false as a Boolean expression. On the basis of the evaluation of the Boolean condition, the entire expression returns value_if_true if condition is true, but value_if_false otherwise. Usually the two sub-expressions value_if_true and value_if_false must have the same type, which determines the type of the whole expression. The importance of this type-checking lies in the operator's most common use—in conditional assignment statements. In this usage it appears as an expression on the right side of an assignment statement, as follows:
variable = condition ? value_if_true : value_if_false
The
?:
operator is similar to the way conditional expressions (if-then-else
constructs) work in functional programming languages, like Scheme, ML, and Haskell, since if-then-else forms an expression instead of a statement in those languages.
I think your specific problem is related to this:
As in the if-else construct only one of the expressions 'x' and 'y' are evaluated.
If you read through the above link on ternary
expressions you'll see that evaluation is short-circuited so your assignment on the false side errors because 1 = true
.
In any case, it doesn't matter too much because I don't think this does what you think it does.
-
As WP says,
$(())
can only contain arithmetic expressions and therefore, a general ternary assignment doesn't exist in Bash.zakmck– zakmck2023年04月10日 18:49:47 +00:00Commented Apr 10, 2023 at 18:49 -
@zakmck i think you should check again. try the code in the example.mikeserv– mikeserv2023年09月24日 02:07:55 +00:00Commented Sep 24, 2023 at 2:07
-
@mkeserv, I usually check before commenting, now your turn: Works: echo $((1==2 ? 1 : 0)). Works, but accidentally: echo $((1==1 ? 1 : NO)). Doesn't work, evaluates $NO to 0, returns 0, not 'NO': echo $((1==2 ? 1 : NO)). Error: $((1==2 ? 1 : 'NO')). Error, "A" and "B" are evaluated to 0, result is 1: echo $(("A" == "B" ? 1 : 0)). The problem is $((x ? y : z)) is valid for numerical expressions only. Bash 5.1.8(1)zakmck– zakmck2023年09月24日 15:29:12 +00:00Commented Sep 24, 2023 at 15:29
-
you cant assign to a number. the assigned variable has to be the first in the list.
$((no?1:2))
and of course the ternary is valid for mumericals only; its arithmetic.mikeserv– mikeserv2023年09月24日 21:10:38 +00:00Commented Sep 24, 2023 at 21:10 -
the point is $(( ? : )) works when returning numbers only and the OP probably wants to know if such a ternary operator exists in general (as other say, it doesn't).zakmck– zakmck2023年09月25日 21:37:23 +00:00Commented Sep 25, 2023 at 21:37
Ternary operators return a value based on a test. They are not used for branching.
Here's one way to get a pseudo-ternary operator for bash (note the back-ticks):
$result=`[ < condition > ] && echo "true-result" || echo "false-result" `
$ a=
$ c=`[ $a ] && echo 1 || echo 0`
$ echo $c
0
$ a=5
$ c=`[ $a ] && echo 1 || echo 0`
$ echo $c
1
((a)) && b=1 || c=1
ternary
operators indash, zsh, sh
, andbash
and they all behave the same, despite their not being specified by POSIX. Your example only works inbash
andzsh
as far as I know. However, this is POSIX friendly:( : ${a?} ) && b=1 || c=1
. I also find it far easier to read than eitherternary
or your own example.