2

Contrarily to ksh, that allows multidimensional arrays, bash (4.2 or below) does not allow multidimensional arrays. I work around this by having a separator in the elements and then each element of the first array can be made in to an array itself. Foe example:

GPIO=("in:down:0:22:1-1.4:17:usb:usb port 3 mgmt button"
"in:down:0:13:1-1.5:18:usb:usb port 4 mgmt button")

then to access the third element of the first row I'd do something like this:

eval $(echo "ROW0=($(echo "${GPIO[0]}" | awk -F: '{ for(i=1;i<=NF;i++) printf("\"%s\" ",$i);}' ))")
echo ${ROW0[2]}

I don't like doing it but I've not found a neater way around the problem. Is there a neater and more efficient way around the problem while still approaching it like an array ?

There is also a workaround using associative arrays and adding a fake second index in the element index:

declare -A FAKE2DIMARRAY
FAKE2DIMARRAY=(
[0,0]="first row first element"
[0,1]="first row second element"
[1,0]="second row first element"
[1,1]="second row second element"
)

Although this solution is really neat and efficient it makes the definition of the array a mess if it has many rows and elements. For instance my full GPIO array is actually 16x8 and that would require 128 lines and taking care to correctly index each line ... that's less then optimal too in this case.

louigi600
  • 716
  • 6
  • 16
  • see if http://stackoverflow.com/questions/11233825/multi-dimensional-arrays-in-bash and http://stackoverflow.com/questions/16487258/how-to-declare-2d-array-in-bash help... and is it a typo in your title? `mono` instead of `multi` – Sundeep Sep 28 '16 at 10:55
  • 1
    Read the title as : "a neater workaround for bash mono dimensional array limitation" Both links use the faked multidimensional ... the second one has an interesting approach though ... but is limiting to what you put in the rows. – louigi600 Sep 28 '16 at 12:21
  • 2
    Needing nested data structures is a very good sign you need to start using a different language. – chepner Sep 28 '16 at 12:48
  • Yes and no ... kindof ;) but I don't want to write this in another language yet :) – louigi600 Sep 28 '16 at 12:57

1 Answers1

3

It is not so difficult to have an automatic creation of a (fake) multidimensional fullarray. To extract indexes from the array: look at the end.

Use the strings as you have them now to create several arrays:

IFS=: read -a arr0 <<<"in:down:0:22:1-1.4:17:usb:usb port 3 mgmt button"
IFS=: read -a arr1 <<<"in:down:0:13:1-1.5:18:usb:usb port 4 mgmt button"

Just make them start with the same name, arr in this example.

Then, the automatic system that will take all variables which names start with arr for rows.
And the count of values in such arrays as columns:

#!/bin/bash
IFS=: read -a row1 <<<"in:down:0:22:1-1.4:17:usb:usb port 3 mgmt button"
IFS=: read -a row2 <<<"in:down:0:13:1-1.5:18:usb:usb port 4 mgmt button"

#echo "one ${row1[@]}"
#echo "two ${row2[@]}"

declare -A fullarray
# Note that ${!row*} lists all the variables that start with arr.
for hrow in ${!row*}; do
    #echo "row $hrow"
    for (( column=0; column<${#row1[@]}; column++ )); do
        #echo "column $column"
        indirect=$hrow[$column]
        #echo "indirect $indirect ${!indirect}"
        fullarray[${hrow#row},$column]=${!indirect}
    done
done

declare -p "fullarray"

Un comment the echo(s) to see how the array is built.

As you can see from the output of declare -p fullarray, the variable has all the values in the array (hrow,column) and each value could be accessed with fullarray[$hrow,$column] if row and column are numbers from 0 and up.
In this case keep the $ in the variables since we need the string representation of the indexes (not their numeric equivalent).


Of course, I guess that the assignment to an array of each row already solve your problem without needing to use awk.

That only depends if using:

indirect=arr$row[$column]
${!indirect}

is good enough for you. :-)


To extract the indexes from the full array, if that is needed, you could do in three steps. First get all the indexes from fullarray:

allind=(${!fullarray})

And, as allind will be an array in itself, you could use the comma as a separator to extract each part:

rowind=( ${allind[@]//,*} )

As the rowind will contain all row indexes for all columns, the values will be repeated. Just sort and remove repeats:

rowind=( $(printf '%s\n' "${rowind[@]}"|sort -u) )

And, if you want, take a look at the definition of row indexes:

declare -p rowind

The same process (except that allind has already been defined) for columns:

allind=(${!fullarray[@]})
columnind=( ${allind[@]//*,} )
columnind=( $(printf '%s\n' "${columnind[@]}"|sort -u) )
declare -p columnind
  • Nice I like that ... and it avoids all the piping each time you access row data (which is a performance killer). I'll probably prefix the names of the arrays for each row "ROW" instead of "arr". Can also do without read -a and just declare the 16 rows and then turn them into a fake multidim array ... I think I'll go for this ... it will be easier to read the config file (sourced into the script). – louigi600 Sep 28 '16 at 12:29
  • I cleaned up the script last night (now GPIO is built up from PORT flike fullarray) and I improvised " for PORT in $(tr -d "PORT" <<< ${!PORT*}) " to get the port number list from the arrays that contain the row data. I was wondering if it's possible to get that in a cleaner way ? Or even directly from the fake multidim array ? Is there a way to do something like ${!GPIO[*,0]} to get all the first column index names of each row ? – louigi600 Sep 29 '16 at 06:30
  • ${!PORT*} woild have all the variable names that start with PORT ${!fullarray[*]} would have all the indexes in fullarray. What I wanted is have is a subset of all the indexes because it's fake multidim (to emulate returning just the first column index names of each row). – louigi600 Sep 30 '16 at 06:42
  • @louigi600 I hope that the edit I just did will answer your additional request. –  Sep 30 '16 at 17:06
  • @louigi600 It would be nice if you select this answer if it solved your problem. [What should I do when someone answers my question?](http://stackoverflow.com/help/someone-answers) –  Oct 01 '16 at 17:31
  • I like your solution ... but the column and row index retrieval you suggest uses more command substitutions and more pipes then my improvised solution.... but works even if you know not how the fake multidim array was created. – louigi600 Oct 04 '16 at 07:45