1

I have a 2 dimensional array in bash and I am able to successfully initialize it, but when I choose to set a value to a certain element in the array- the script sets it to ALL the elements in the array ! Baffled ! I was hoping it would set to just that one element.

Here's the scenario - I have a 2 D array for all the 12 months in a year. ( let's say for year 2012. ) I am able to initialize it to '-1'. Later at some time in the code I want to set just the 6th month to say 1000, then the 'initializing' sets the 1000 to all the elements in the array. Why ? I'm on a Mac OS Yosemite 10.10.5 and running this as /bin/bash

declare -a outages

function init_array () {
    for (( month=1; month<12; month++)) do
        outages[$month,12]=-1
    done
    outages[6,12]=1000 # this is setting all elements to 1000, instead of just 1.
}
function print_array () {

    for ((i=1;i<=12;i++)) do
         echo  outages[$i,12] = ${outages[$i,12]}
    done

}
init_array
print_array

Results Cut and Pasted below.
./test.sh 
outages[1,12] = 1000
outages[2,12] = 1000
outages[3,12] = 1000
outages[4,12] = 1000
outages[5,12] = 1000
outages[6,12] = 1000
outages[7,12] = 1000
outages[8,12] = 1000
outages[9,12] = 1000
outages[10,12] = 1000
outages[11,12] = 1000
outages[12,12] = 1000
2
  • Always try debugging with a set -x option in cases like these. If you had run like that it will be obvious for you to understand that you were operating the array as a one-dimensional entity. Commented May 12, 2016 at 8:40
  • If you need a 2-dimensional array (or even nested arrays), you are using the wrong language. Commented May 12, 2016 at 11:53

1 Answer 1

2

Bash has two types of arrays: one-dimensional indexed and associative array. declare -a creates a one-dimensional array. To mimic a 2-D array, you need an associative array. Therefore, replace:

declare -a outages

with:

declare -A outages

With that one change:

$ bash test.sh
outages[1,12] = -1
outages[2,12] = -1
outages[3,12] = -1
outages[4,12] = -1
outages[5,12] = -1
outages[6,12] = 1000
outages[7,12] = -1
outages[8,12] = -1
outages[9,12] = -1
outages[10,12] = -1
outages[11,12] = -1
outages[12,12] =

(Note that outages[12,12] is never set because the loop is limited by month<12.)

What happens when you provide a 2-D subscript to a 1-D array

As in the original code, let's declare a 1-D array and try to use it as a 2-D array and see what happens:

$ declare -a outages
$ outages[1,2]=-1

This looks like it made a successful assignment to a 2-D array. But, that is not what happened. To see what really happened, use declare -p:

$ declare -p outages
declare -a outages='([2]="-1")'

We can see that element 2 was assigned a value, not element 1,2. The reason is that subscripts of 1-D arrays are treated as arithmetic expressions. Under the rules of arithmetic evaluation, anything before a comma is discarded.

One can see this by looking directly at an arithmetic expression:

$ echo $((1,2))
2

Everything before the , is discarded. That is why setting outages[6,12]=1000 had the effect of setting all your other ,12 elements to 1000.

Documentation

From the section of man bash entitled "ARITHMETIC EVALUATION":

The operators and their precedence, associativity, and values are the same as in the C language.

One needs to refer to C language documentation, such as here, to find out what the comma operator actually does.

Simulating 2-D array with bash 3.x

There are some hacks to shoehorn 2-D-like features into bash 3.x's 1-D arrays. See, for example, here or here. In this case, however, your subscripts are numeric and simpler approaches are possible. Let's update the code like this:

$ cat test2.sh
declare -a outages

function init_array () {
    for (( month=1; month<=12; month++)) do
        outages[$month+100*12]=-1
    done
    outages[6+100*12]=1000 # this is setting all elements to 1000, instead of just 1.
}
function print_array () {

    for ((i=1;i<=12;i++)) do
         echo  "outages[$i,12] = ${outages[$i+100*12]}"
    done

}
init_array
print_array

The code runs as you want:

$ bash test2.sh
outages[1,12] = -1
outages[2,12] = -1
outages[3,12] = -1
outages[4,12] = -1
outages[5,12] = -1
outages[6,12] = 1000
outages[7,12] = -1
outages[8,12] = -1
outages[9,12] = -1
outages[10,12] = -1
outages[11,12] = -1
outages[12,12] = -1

We can see what the array outages looks like to bash with declare -p:

$ declare -p outages
declare -a outages='([1201]="-1" [1202]="-1" [1203]="-1" [1204]="-1" [1205]="-1" [1206]="1000" [1207]="-1" [1208]="-1" [1209]="-1" [1210]="-1" [1211]="-1" [1212]="-1")'
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the response. I am running this on Mac OS 10.10.5 Yosemite and declare -A is not recognized. ./test.sh: line 2: declare: -A: invalid option declare: usage: declare [-afFirtx] [-p] [name[=value] ...] outages[1,12] = 1000 bash -version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14) Copyright (C) 2007 Free Software Foundation, Inc.
Oops. Good point. As you correctly found, Bash 3.2 does not have associative arrays. They were added to bash version 4 which was released in 2009.
@ShashiKiran I just updated the answer with a workaround that might work for you.
Thanks John - I voted for your answers and thanks for the help. Much appreciated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.