[SOLVED] get var value when var name is part of another var
Linux - NewbieThis Linux forum is for members that are new to Linux.
Just starting out and have a question?
If it is not in the man pages or the how-to's this is the place!
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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?
#!/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
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.
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.
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.
#!/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.
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, varlistis 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.
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).
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
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.
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.
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? Thank you for discussing with me.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.