Variables in array names


#1

Simple question, but something thats been biting me for years. Hope someone can explain a fixed method for always doing this right.
Variables within variables within variables …

Lets say I have a set of arrays containing data.
The arrays are called
Column_1
Column_2
etc
Column_n

So if I want to call one of the arrays say Column_1 with a function, then I will do
x=("${column_1[@]}")
and call it
func x

Problem arises when I want to replace !..n with a variable.meaning
x=("$column_$index[@]") where index an element of {1…n}
It fails miserably, and I cant figure out how to make x receive the correct array name through this variable.

Thanks


#2

useless. simply call func column_1

I would have done the opposite, putting rows in arrays and call columns using the index…


#3

I think you misunderstand me. The question is about how to predictably call arrays which are indexed by a variable in the name

Here is a working example, which I need the commented part to work. The problem is about variables within function names which I need to get a reliable and understandable way to resolve to the same thing . e.g. x=("${column_$index[@]}") must resolve to the same x=("${column_1[@]}") when index=1

See below

#!/bin/bash
func () {
declare -n a=$1
echo "printing array a[@]"
for i in ${a[@]}
do
echo $i
done
}

#------------------Main Program--------------------------------
Col_1[1]=1;
Col_1[2]=2;
Col_1[3]=3;
Col_2[1]=4;
Col_2[2]=5;
Col_2[3]=6;
x="${Col_1[@]}" #obviously works when called by func as func x

#but I want to call
x=("${Col_$index[@]}")
#where $index can be 1 or 2 e.g.

func x


#4

I feel like you’re trying to do things the way another language does, not the way bash does.
you’re trying to twist it until it fits into your square.
obviously, it doesn’t fit, then change the shape of your algorithm.


#5

If you dont want to contribute to this thread then just dont. We all have our squares as that is what the particular problem you encounter requires. It is a reasonable straight forward elementary question and something that I see no reason bash cannot do… Either bash cannot or can do it. If it cannot, I want to know the reason why. If it can, I want to learn how.


#6

re-thinking…
as you’re giving function the name of the array (not the array’s content, that’s why x is useless!), try myFunc arrayName_$var

if you want to learn bash, you have to learn the way it works, not applying blindly your previous knowledge.


#7

Look lets just leave the stabs and flames. I have been using bash for 20 years, and now just try to work through the irritations and misunderstandings I found along the way. You dont know who I am and where I come from so just lets stick to the subject and try to resolve an issue in a non personal way.

Back to the topic.
Where do I call the function name the same as the array name ?
The function is called “func” and the arrays are Col_n


#8

Look the problem I asked to have resolved is the following

x=("${Col_1[@]}") which I want to rep[lace with a variable for the “1” as
something like
x=("${Col_$index[@]}") in order that I can feed the function with different arrays assigned to x as I change index.
Thats all… no rocket science.
x=("${Col_1[@]}") works PERFECTLY when called by my function and parses all the data correctly. However I want to put x=("${Col_1[@]}") and the function call in a LOOP so I need to cycle the “1” to 2 and 3 and 4 …
Obviously the Variable $index needs to be treated by some bash magic so that
if I do
index=1
then
x=("${Col_$index[@]}") must equate exactly the same as
x=("${Col_1[@]}")
The problem is that I dont know how to forge $index in the the array x-expression to have bash do this .
is it
x=("${Col_${!index}[@]}" ??
(Which fails as a bad substitution obviously)
or
x=("${Col_‘index’[@]}" ?? etc etc…
or what ?
I mean this type of trivial variable within variable thing happens in bash all the time, but I have trouble with arrays in this way.


#9

you’re over complicating things.

do as I tell you:

array_1=( a b c )
myFunc() {
   declare -n insideArray=$1
   for i in "${insideArray[@]}"; do echo "$i"; done
}
n=1
myFunc array_$n

KISS!


#10

That is a trivial example everyone knows that works only for arrays defined in that way.
It doesnt work at all for me as I load the arrays as
column_Col_8+=(${!comp})
where comp is in a loop reading from a file and then pass the array e.g. with
x=(“column_Col_2[@}”).
func x
Works ABSOLUTELY great except for indexing the integer in the name.

LOL Thanks but try that on your girlfriend/boyfirned you dont have.
Keep him/her stupid simple.


#11

Please … al I want is if I set index=1 or 2 or 3 … in x=("$column_$index[@]")
it needs to set x=(“column_1[@]”) or x=(“column=2[@}”).

All I need is a direct answer on how to do x=("$column_$index[@]") so that x=(“column_1[@]”)

As I mentioned my program works great feeding the function “func” with
x=("$column_1[@]")
func x
So it is valid bash !
but I need to have the integer made a variable along the lines of x=("$column_$index[@]")
Thats is the TOTAL scope of the question.


#12

what difference does it make to pass x (which doesn’t work!) or column_Col_$n (which, I’ve showed you, works) ?

when you’re old enough I’ll tell about birds and bees. :wink:


#13

OK, what about this? It only works for simple value variables, so no strings.

#!/bin/bash

print_array() {
for i in ${@}; do
echo $i
done
}

Variables

COL_1[1]=1
COL_1[2]=2
COL_1[3]=3
COL_2[1]=4
COL_2[2]=5
COL_2[3]=6
COL_3[1]=7
COL_3[2]=8
COL_3[3]=9
COL_3[4]=10
COL_3[5]=11

for VAR in 1 2 3; do
TMPVAR="COL_${VAR}[@]"
print_array "${!TMPVAR}"
done

The trick appears that you need to first build the name of the variable you wish to indirectly reference in another variable then do the indirect reference.


#14

It makes a HUGE difference
Because my programs work perfectly but because I LOAD the arrays as I explained your generic solution fails when called by any function but mine works.
Since I have lots of these routines which works like that and which I dont want to change, I want to find a solution by which I can put the function call in a loop.
For that I need to replace the integer with an index in some way I dont know and maybe a bash wizard knows.
x=("$column_$index[@]") must resolve to x=("$column_1[@]")

By now I explained it about 10x
Ok Im old enough tell me about the birds and the bees.


#15

YOU sure get what I am talking about and what my problem is based on you comment. Exactly, first needs to build the name variable which is the problem I have. I tried “massaging” a new name that would resolve the same several times but failed every time.
My programs work perfectly, just ran into this unexpected indexing problem.
Thanks I will check.


#16

OK, small change to the print_array function to put its variables into an array and get array length output.

print_array() {
declare -a a=( $@ )
echo "Array Length: ${#a[@]}"
for i in ${a[@]}; do
echo $i
done
}

Seems an unnecessary step but the values can be put back into an array in the function. Overall I wonder if watael wasn’t partially correct, this feels like it may be too complicated to support and maintain long term. There might be a better solution out there.


#17

That is what the problem is with just deciding what other people need or trying to force them into another cubicle of thought.
The bash program in question has been servicing a large numerical program on a cluster for a long time doing only the file IO for Lazarus and Fortran programs I wrote. Reason to use bash for me was since Fortran/Lazarus/Pascal/C/C++ has all absolutely horrible file IO and some like Fortran has no noticeable user support, literally taking months to get an answer for a bug posted. From the blatant unforgiving strongtype file IO of fortran/pascal/lazarus to the red=herring type-forkbombclusterF of C/C++, Bash believe it or not, is way better for me at fileIO. It is halfway between the problems mentioned above and served me very well.
Some of the computations take 2 days to complete and therefore minimum fiddling with code needs to be done as checking the entire thing can take 2 days …
The minimum intrusive way to update the program and make it more manageable is just this… to index the existing array definitions which works flawlessly.
So I can either just get the indexing done and all will work as it did and no testing necessary or … I could go fiddle with new array definitions new funky function calls and array definitions, new rubik-cube thoughts and spend two years getting back up to speed.


#18

However complicated it may be, this may very well be the best option for your needs given what you have described. I just know my memory sucks at times so if I had to use a solution like this I’d be sure to include a few comments on why and how it all worked. Nothing worse when facing a problem in the future and you can’t remember why you did what you did in other scripts.


#19

Nope, you are right on all accounts except sooooo wrong regarding this personal opinion where you are dead wrong as nobody has a clue about what is and isnt complex in the program without seeing the code to realize why things are defined as they are…
Good news is you actually helped me solve it and it proves my code is valid bash.

  1. I write valid bash code. Complexity has nothing to do with it as that was what is needed to do the job and reading arrays in that way made the task of collecting humongous amounts of data easy.
  2. Your easy solution above solved my problem and I can see now why I ran in a brick wall. In order to have variables in array names you need to add them in a loop with the function call. It is not going to work outside a loop as that is how bash arrays work. Dont know why I didnt get to that as I know this since my functions use loops to access array components and all such things so it is the logical solution to try. I was trying to do it in a straight variable substitution which was the flaw in my thinking.

So the solution based on your suggestions is

for((i=0;i<${#R[@]};i++)); #Where R is the location and range of data sets
do #Only plot x vs x as test
TMPVAR=“column_Col_${i}[@]“
x=(”${!TMPVAR}”)
y=("${!TMPVAR}")
func x y 1
done

Silly simple ! and it all works just great.

Well thanks a lot "lowkey"
SOLVED: You are the WIZARD!!


#20

You’re welcome but I’m not a wizard. Like you, I’m just someone who has found that Bash is powerful enough to do what I need far too often.