6

cat test.sh

#!/bin/bash
key="index";
arr[$key]="val"
echo ${arr[${key}]}

/bin/bash-x test.sh

+ key=index
+ arr[$key]=val
+ echo val
val

then I modify the test.sh:

#!/bin/bash
key="index.index";
arr[$key]="val"
echo ${arr[${key}]}

/bin/bash -x test.sh

+ key=index.index
+ arr[$key]=val
test.sh: line 3: index.index: syntax error: invalid arithmetic operator (error token is ".index")
test.sh: line 4: index.index: syntax error: invalid arithmetic operator (error token is ".index")

why this error appears, any suggestion will be appreciate!

Tim
  • 659
  • 1
  • 7
  • 16

3 Answers3

7

This:

key="index";
arr[$key]="val"
echo ${arr[${key}]}

only appears to work. Since arr is an ordinary array, not an associative array, it can only be indexed by non-negative integer values.

Consider this valid code:

index=42
key="index"
arr[$key]="val"
echo ${arr[42]}
echo ${arr[index]}
echo ${arr[$index]}
echo ${arr['index']}
echo ${arr["index"]}
echo ${arr[\index]}

All the echo statements print val. Since the index is treated as an arithmetic expression, it can refer to a variable (in this case, $index) either with or without the $ prefix -- even if it's a quoted string.

In your code, where you never assigned a value to $index, ${arr[${key}]} expands to ${arr[index]}, which is equivalent to ${arr[$index]}, which is treated (by default) as equivalent to ${arr[0]}.

(If you have set -o nounset, then references to unset variables are treated as errors, and your code will produce an error message.)

Your second chunk of code:

key="index.index";
arr[$key]="val"
echo ${arr[${key}]}

is invalid because index.index is not a valid variable name -- even though you probably meant it to be just a string used as an array index.

If you want arr to permit arbitrary strings as indices, it needs to be an associative array. You can create a non-associative array simply by assigning to it (or by using declare -a), but an associative array can only be created with declare -A.

Associative arrays were added to bash in version 4. If you're using an earlier version of bash, declare -A is not supported. You'll need to upgrade to a newer bash, code up some clumsy alternative, or use a language that does support associative arrays, like Awk, Python, or Perl.

Adding declare -A arr (as user000001's answer suggests) should solve the problem (if you have bash 4), but it's instructive to understand what your original code is actually doing (or rather not doing).

(BTW, thanks for asking this; I learned a lot as I was composing this answer.)

Community
  • 1
  • 1
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
6

Declare the array variable as an associative array with declare -A arr.

$ cat test.sh 
#!/bin/bash
set -x 
declare -A arr
key="index.index";
arr["$key"]="val"
echo "${arr["${key}"]}"

$ ./test.sh 
+ declare -A arr
+ key=index.index
+ arr["$key"]=val
+ echo val
val
user000001
  • 32,226
  • 12
  • 81
  • 108
  • 5
    It's worth noting that before, the string "index" was being treated as an unset variable when used as an index, and so evaluated to 0. – chepner Aug 15 '13 at 13:09
1

This behavior varies by BASH version. Older BASH versions only allowed non negative integers as the keys in arrays.

Please note that dot/period is not allowed in a variable name in BASH.

See this Q&A for more details about allowed characters in BASH: Allowed characters in linux environment variable names

EDIT:

(Many thanks to @chepner for this addendum)

Regular arrays (not associative array) are indexed by integer only. Any expression used as an index between square brackets is treated as an arithmetic expression. $key expands to index, which is then treated as an (unset) variable which expands to 0. If you assigned a value of, say, 3 to index, then ${array[$key]} -> ${array[index]} -> ${array[3]}. It's a type of implicit indirect parameter expansion

Community
  • 1
  • 1
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    Does this also apply for associative arrays? Because it seems to work when you `declare -A` the array. – user000001 Aug 15 '13 at 11:21
  • It didn't work for me in `GNU bash, version 4.1.2`. Which BASH version do you have? – anubhava Aug 15 '13 at 11:24
  • I have `GNU bash, version 4.2.25(1)-release (i686-pc-linux-gnu)`. Strange – user000001 Aug 15 '13 at 11:28
  • 2
    Regular arrays (not associative array) are indexed by integer only. Any expression used as an index between square brackets is treated as an arithmetic expression. `$key` expands to `index`, which is then treated as an (unset) variable which expands to 0. If you assigned a value of, say, 3 to `index`, then `${array[$key]} -> ${array[index]} -> ${array[3]}`. It's a type of implicit indirect parameter expansion. – chepner Aug 15 '13 at 13:13
  • Your first paragraph is incorrect. For an ordinary array, indices can only be numbers. For an associative array (which can only be created with `declare -A`), indices can be any arbitrary strings. `declare -A assoc ; assoc[foo.bar]=42 ; echo ${assoc[foo.bar]}` prints `42` – Keith Thompson Aug 15 '13 at 15:18
  • @KeithThompson: What you're saying is correct for newer BASH versions. On my BASH version (4.1.2) even `declare -A` didn't work as explained in chepner's comment. – anubhava Aug 15 '13 at 16:15
  • 1
    You're right; associative arrays were added to bash in version 4. But there's still nothing special about the `.` character in the key/index of an array. Variable names must be identifiers, which means they can't contain `.`. Indices for ordinary arrays must be non-negative integers; there's nothing special about `.`, they can't contain any punctuation other than `_`. Indices for associative arrays (which exist only in bash 4 and higher) can be any arbitrary string; again, there's nothing special about `.`. – Keith Thompson Aug 15 '13 at 17:48
  • Adding correct information after incorrect information, but leaving the incorrect information there, is hardly a way to correct a bad answer. – Charles Duffy Aug 15 '13 at 18:57