Bash's conditional operator and assignment

30,294

Solution 1

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) ))

Solution 2

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.

Solution 3

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

Share:
30,294

Related videos on Youtube

MS.Kim
Author by

MS.Kim

Updated on September 18, 2022

Comments

  • MS.Kim
    MS.Kim almost 2 years

    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?

    • Admin
      Admin about 10 years
      @devnull I think the code's readability depends most upon who reads it. Still, it may be true what you say, but I've tested ternary operators in dash, zsh, sh, and bash and they all behave the same, despite their not being specified by POSIX. Your example only works in bash and zsh as far as I know. However, this is POSIX friendly: ( : ${a?} ) && b=1 || c=1. I also find it far easier to read than either ternary or your own example.