U. Windl
U. Windl

Reputation: 4325

bash `[[` evaluation rules

It seems [[ uses rather different evaluation rules that plain test ([) does. However those rules are not properly described in the bash manual (IMHO). Consider this example:

> a[1]=''
> i=1
> [[ -v a[i] ]] && echo true || echo false
true
> [[ -v a[$i] ]] && echo true || echo false
true
> [[ -v a[$j] ]] && echo true || echo false
false
> i=0
> [[ -v a[$i] ]] && echo true || echo false
false
> [[ -v ${a[$i]} ]] && echo true || echo false
false
> i=1
> [[ -v ${a[$i]} ]] && echo true || echo false
false

So the question is: *Where can I leave out the $, where must I use the $, and where can I use the $ inside [[ (without affecting the correct result)?

On "The subscript is treated as an arithmetic expression that must evaluate to a number"

(https://stackoverflow.com/a/74401112/6607497 says (via https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Arrays)):

The subscript is treated as an arithmetic expression that must evaluate to a number

It seems to me that when using associative arrays, then the key must be expanded using a $. Consider this set of commands:

> a[k]=k
> declare -p a
declare -a a=([0]="k")
> declare -A A
> A[k]=k
> declare -p A
declare -A A=([k]="k" )
> [[ -v a[k] ]] && echo true || echo false
true
> [[ -v A[k] ]] && echo true || echo false
true
> [[ -v a[j] ]] && echo true || echo false
true
> [[ -v A[j] ]] && echo true || echo false
false
> j=k
> [[ -v a[j] ]] && echo true || echo false
true
> [[ -v A[j] ]] && echo true || echo false
false
> [[ -v A[$j] ]] && echo true || echo false
true

Upvotes: 0

Views: 52

Answers (1)

KamilCuk
KamilCuk

Reputation: 140930

One of the confusing things is that a[i] seems to be treated the same as a[$i], but it seems if i is not a number, then I need the $

From https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Arrays :

The subscript is treated as an arithmetic expression that must evaluate to a number

From https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Arithmetic :

Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated. Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax. [...] A null value evaluates to 0. [...]

From that:

a[1]=''
i=1
[[ -v a[i] ]]    # Arithmetic expression `i` evaluates to 1.
                 # Element a[1] exists. True.
  
[[ -v a[$i] ]]   # `$i` is expanded -> [[ -v a[1]  ]]
                 # Arithmetic expression `1` evaluates to 1.
                 # Element a[1] exists. True.

[[ -v a[$j] ]]   # `$j` is expanded -> [[ -v a[] ]]
                 # Arithmetic expression '' evaluates to 0. (a null value evaluates to 0)
                 # Element a[0] does not exist. False.

i=0
[[ -v a[$i] ]]     # Expands to [[ -v a[0] ]]
                   # False, a[0] does not exist

[[ -v ${a[$i]} ]]  # Expands to [[ -v '' ]]
                   # False, variable with empty name does not exist.

i=1
[[ -v ${a[$i]} ]]  # Expands to [[ -v 0 ]]
                   # False, variable named `0` does not exist.

This is unrelated to [[. You can do echo "${a[i]}" "${a[i + 1]}" or echo "$(( ))". What [[ does is disables word splitting and filename expansion on the words that are it's arguments.

Note that for example, in the first case, the [[ builtin receives 4 arguments -v a[i] ]]. i is not expanded. Then, [[ builtin internally handles the a[i] expression. In that sense, [[ -v is super special, where the "expansion" of the expression i inside a[i] "happens" inside the command after the builtin was called.

Where can I leave out the $, where must I use the $, and where can I use the $ inside [[ (without affecting the correct result)?

Note that $(( $i )) is effectively double expanding:

a=123
b=a
$(( $b ))   # expands to $(( a ))
            # evaluates `a` to 123

You usually want to leave out $ inside arithmetic expression. Also, there are associative arrays, for which a[$i] and a[i] has different behavior, as the part of [<here>] is no longer an arithmetic expression. So where you "can" leave out depends on the behavior you want to get. Working with indexed arrays, you want to just a[i].

Upvotes: 3

Related Questions