LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   get var value when var name is part of another var (https://www.linuxquestions.org/questions/linux-newbie-8/get-var-value-when-var-name-is-part-of-another-var-4175431919/)

bilyboy65 10-12-2012 04:15 PM

get var value when var name is part of another var
 
Hello,

I'm trying to write a script in bash.

I have a for loop

varlist="one two three"
one_DIR=/directory1
two_DIR=/directory2
three_DIR=/directory3

for var1 in varlist
do
cd $"$var1_DIR"
done

var1 will contain part of a name to another variable that I want to get a value of. So I know the above code is wrong, but it illustrates what I'm trying to do.

I want to change to a dir stored in the n_DIR variable and use the value in var1 to get the variable name.

Is there a way to do this or should I abandon this plan and figure out another way?

Thank you for any help.

suicidaleggroll 10-12-2012 04:29 PM

Code:

#!/bin/bash

varlist="one two three"
one_DIR=/directory1
two_DIR=/directory2
three_DIR=/directory3

for var1 in $varlist; do
  varname=${var1}_DIR
  cd "${!varname}"
done


colucix 10-12-2012 04:38 PM

Indirect variable reference.
Code:

for var1 in $varlist
do
  my_var=${var1}_DIR
  cd ${!my_var}
done

First it builds the name of the variable based on the current value of var1, then indirectly references the variable and gives you the desired result.

Edit: oops... too late! :) Sorry for redundancy.

David the H. 10-13-2012 11:21 AM

Please use ***[code][/code]*** tags around your code and data, to preserve the original formatting and to improve readability. Do not use quote tags, bolding, colors, "start/end" lines, or other creative techniques.

Indirect variables are not a generally recommended practice. It makes the code obscure. Modern shells usually have better ways to do most of the same jobs.

How can I use variable variables (indirect variables, pointers, references) or associative arrays?
http://mywiki.wooledge.org/BashFAQ/006

In addition, you should not store lists of things in a single variable. That's what arrays are for.


In this case, I think all you should really need is a single regular array.

Code:

dirlist=( /directory1 /directory2 /directory3 )

for var in 0 1 2 ; do
        cd "${dirlist[var]}"
done

Remember that arrays start at index 0.

You could work with intermediate variables too, if you wanted, such as

Code:

one=0
cd "${dirlist[one]}".

But a stronger option, if you really need to use strings like "one, two, three", would be to use bash's associative arrays; where the array index is a text string (or a variable containing a text string), rather than an integer.

Code:

declare -A dirlist

dirlist[one]=/directory1
dirlist[two]=/directory2
dirlist[three]=/directory3

for var in one two three; do
        cd "${dirlist[$var]}"
done

Notice that since the index is now a string, The "[]" field is no longer operating as an arithmetic environment, and so the variable needs to have a dollar sign attached to it.

bilyboy65 10-15-2012 11:19 AM

David - Thank you for the help, but I choose a string list for a number of reasons. Maybe I just don't know a trick to accomplish this using array's in bash. But mainly I chose not to use an array because I need to identify what values have been set and the kernal I'm using doesn't allow for the associative arrays (and I can't change this). I read through the links you posted and didn't see an alternative to what i need to accomplish. Searching a string seems to be faster then looping on an array to match a value. I also can't use a hard coded list of directories because that changes with each box being accessed. These have to be setup as parameters. I think the only answer for me is to indirectly access the variable value.

I do appreciate the info on the code tags. I was not aware of those but will be using them in the future.

bilyboy65 10-15-2012 11:21 AM

Quote:

Originally Posted by suicidaleggroll (Post 4804278)
Code:

#!/bin/bash

varlist="one two three"
one_DIR=/directory1
two_DIR=/directory2
three_DIR=/directory3

for var1 in $varlist; do
  varname=${var1}_DIR
  cd "${!varname}"
done


Thank you this is the solution i went with and everything works.

David the H. 10-20-2012 09:35 AM

Quote:

Originally Posted by bilyboy65 (Post 4806261)
But mainly I chose not to use an array because I need to identify what values have been set and the kernal I'm using doesn't allow for the associative arrays (and I can't change this).

What does the kernel have to do with this? This is entirely a shell syntax problem. bash has included support for regular arrays from v.3, and associative arrays from v.4. Unless you aren't using bash at all (or ksh, or another shell that supports arrays).


Quote:

I read through the links you posted and didn't see an alternative to what i need to accomplish. Searching a string seems to be faster then looping on an array to match a value.
One of the main weaknesses with indirect variables is that variable names can only include letters, numbers, and the underscore, and cannot begin with a number. So if the value of var1 included anything outside of that pattern, then "${!varname}" would not give you a valid value.

But if you are defining the variable names/indexes entirely in the script, then this should really be a non-issue, since the problem only occurs if the variable names are defined from outside. If you are only using the names internally, there should be little need for any kind of indirection.

Associative arrays don't suffer from this problem, naturally, as any string can be used as the index, and of course regular arrays simply use numbers to specify the entry.

And I only used a loop for my examples because that's what you used. There's no need to loop through anything if you know the actual value you need to call.

(
PS: Actually, varlist is a place where you should be using an array over a single variable. Again, scalar arrays are not designed for storing lists of things.

Code:

varlist=( one two three )
for var1 in "${varlist[@]}"; do
        ...

)

Quote:

I also can't use a hard coded list of directories because that changes with each box being accessed. These have to be setup as parameters. I think the only answer for me is to indirectly access the variable value.
I see no problem here either. Shell arrays are just another type of parameter. All you have to do is set the one to the other.

Code:

dirarray=( "$1" "$2" "$3" )

printf '%s\n' "${dirarray[@]}"        #prints all existing array values, one per line

cd "${dirarray[2]}"                #changes to the directory given in parameter $3, in this case.

In short, while I don't have full knowledge of what you are trying to do, I see nothing posted so far that would necessarily favor indirect variables over arrays.

bilyboy65 10-20-2012 09:36 PM

Quote:

What does the kernel have to do with this? This is entirely a shell syntax problem. bash has included support for regular arrays from v.3, and associative arrays from v.4. Unless you aren't using bash at all (or ksh, or another shell that supports arrays).

dirlist[one]=/directory1
dirlist[two]=/directory2
dirlist[three]=/directory3
Using arrays would make my life much easier, but it's just not working for me. I'm using bash and if I enter the code above that you gave me before and type set to see how it's defined; it's not defined as typed. It's defined as following

Code:

dirlist[0]=/directory1
dirlist[1]=/directory2
dirlist[2]=/directory3

I did a lot of research on this and the syntax for bash seems to be as you stated before or as

Code:

declare -A dirlist
however all of these places seem to say that it's only included in bash v4. If i do a man declare there is no -A option and I get an error when running it using bash 3.2.25.
So that is not an option for me it seems. If it was then I wouldn't even be asking about how to use indirection.

I'm not worried about what is stored in the variable for the indirection because it's all internal. Users do not have a way to change these values.

David the H. 10-21-2012 04:37 AM

I'm at a loss to see why it isn't working for you. bash 3.2 definitely has regular (numerically-indexed) arrays, although not associative ones. So the "0,1,2" indexed version should be working, at least, but the "one,two,three" one will not.


But what I'm really questioning is whether you need to have "word string" style variables at all, especially since you state that the whole thing is internal. I still suggest that a regular array would suit you just fine. You could even define the indexes it so that the index numbers start with 1 instead of 0.

Code:

dirs=( [1]=/directory1 [2]=/directory2 [3]=/directory3 )

#print individual entries by index number
echo "The first directory is ${dirs[1]}"
echo "The second directory is ${dirs[2]}"
echo "The third directory is ${dirs[3]}"

#loop and print all entries by value
for dir in "${dirs[@]}"; do
        echo "The directory is $dir"
done

#loop and print all entries by index number
for dir in "${!dirs[@]}"; do
        echo "The current directory is ${dirs[dir]}"
done

And if you really want to use word strings, then you can define variables that expand to the index numbers you want, in a kind of simulated associative array. This would be hard to do safely if the index values could come from outside, but it's not problem in a controlled situation like this.

Code:

one=1
two=2
three=3

echo "The first directory is ${dirs[one]}"
echo "The second directory is ${dirs[two]}"
echo "The third directory is ${dirs[three]}"

for dir in one two three; do
        echo "The directory is ${dirs[dir]}"
done

But anyway, if you feel more comfortable using indirect references, I'm not going to argue it any more. It's your code after all. :)

bilyboy65 10-21-2012 09:32 AM

I appreciate the arguing. It makes me a better programmer and gives me new ways to think about things. And I think with your last idea about assigning numbers to strings gave me an idea of how I can change all of the affected code to use regular arrays. I don't think assigning numbers to strings will be the solution, but it put me on the right path. Who says arguing is a bad thing? :D Thank you for discussing with me.


All times are GMT -5. The time now is 08:14 PM.