18

Let's say I have a variable's name stored in another variable:

myvar=123
varname=myvar

Now, I'd like to get 123 by just using $varname variable. Is there a direct way for that? I found no such bash builtin for lookup by name, so came up with this:

function var { v="\$$1"; eval "echo "$v; }

so

var $varname  # gives 123

Which doesn't look too bad in the end, but I'm wondering if I missed something more obvious.

oguz ismail
  • 1
  • 16
  • 47
  • 69
inger
  • 19,574
  • 9
  • 49
  • 54
  • This is marked as a duplicate of a question that was asked 6 years later? Also IMHO this question+answers is clearer than the linked ones. – inger Sep 01 '17 at 23:30
  • Btw, where is the previous answer? @tripleee – mariotti Jan 30 '19 at 00:57
  • @mariotti I don't understand the question. The yellow box at the top of this page links to a duplicate question with 7 answers. I mainly went with the number of upvotes but you'll note that the duplicate in turn is also a duplicate. Perhaps this one should be marked as a duplicate of the one at the end of the chain instead. Age of questions is usually not a deciding factor when establishing that something is a duplicate. – tripleee Jan 30 '19 at 06:26

5 Answers5

22

From the man page of bash:

${!varname}

If the first character of parameter is an exclamation point, a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
tangens
  • 39,095
  • 19
  • 120
  • 139
  • 1
    Of course, that's it. Tried to RTFM, but somehow this description: ----------------- ${!prefix*} ${!prefix@} Names matching prefix. Expands to the names of variables whose names begin with prefix, separated by the first character of the IFS special variable. When @ is used and the expansion appears within double quotes, each variable name expands to a separate word. ----------------- didn't strike me as the obvious answer. Cheers for that! – inger Nov 07 '09 at 20:07
  • @DigitalRoss: The question was is tagged with "bash", so why do you want to restrict the answer to posix standard? – tangens Nov 07 '09 at 20:10
  • 1
    It's a fine answer, and yes it does apply to bash. I guess the perfect answer would announce `${!varname}` and then note the portability issue to keep the OP out of possible trouble. – DigitalRoss Nov 07 '09 at 20:16
  • agreed, your answers together makes a perfect answer:) – inger Nov 07 '09 at 20:27
  • @inger: `${!prefix*}` and `${!prefix@}` are different from `${!prefix}` – Dennis Williamson Nov 07 '09 at 20:58
  • I down voted in favour of the answer from @DigitalRoss as it considers posix and the fact we might not be using a common bash. As it was not a requirement in the question either. – mariotti Jan 16 '17 at 16:55
  • @mariotti you shouldn't downvote a legitimately good answer. – Eric Walker Dec 29 '17 at 17:36
  • @EricWalker You are probably right. I cannot change the vote until a new editing, so I will wait. – mariotti Jan 02 '18 at 14:28
16

There isn't a direct Posix-conforming syntax, only a bashism. I usually do this:

eval t="\$$varname"

This will work on any Posix shell, including those systems where bash is the login shell and /bin/sh is something smaller and faster like ash. I like bash and use it for my login shell but I avoid bashisms in command files.


Note: One problem with writing bash-specific scripts is that even if you can count on bash being installed, it could be anywhere on the path. It might be a good idea in that case to use the fully general /usr/bin/env shebang style, but note that this is still not 100% portable and has security issues.
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
1

${!varname} should do the trick

$ var="content"
$ myvar=var
$ echo ${!myvar}
content
Arkaitz Jimenez
  • 22,500
  • 11
  • 75
  • 105
  • Thanks, seems your answer arrived just a moment later than tangens, so I selected that one:) – inger Nov 07 '09 at 20:24
1

I usually look at Advance Bash-Scripting Guide when I need to freshen up my Bash skills.

Regarding your question look at Indirect References

Notation is:

Version < 2
\$$var

Version >= 2
${!varname}
Peter Lindqvist
  • 10,122
  • 3
  • 41
  • 60
  • Hmm, glanced that guide quickly - apparently too quickly:) Seem like I have to read through it properly.. thanks for the tip! – inger Nov 07 '09 at 20:22
  • It's my quick reference of choice, i tend to use it more frequently than the man pages. But as you say, a quick glance is sometimes not enough. – Peter Lindqvist Nov 07 '09 at 20:30
  • In general, the ABS should be taken with a huge grain of salt. Nothing wrong with this answer per se, though. – tripleee Aug 01 '16 at 06:54
0
# bmuSetIndirectVar()
# TO DOUBLE CHECK THIS COMMENT AND DEMO
# This function is an helper to read indirect variables.
# i.e. get the content of a variable whose name is saved
# within an other variable. Like:
# MYDIR="/tmp"
# WHICHDIR="MYDIR"
#       bmuSetIndirectVar "WHICHDIR" "$MYDIR"
#
bmuSetIndirectVar(){
    tmpVarName=$1
    locVarName=$1
    extVarName=$2
    #echo "debug Ind Input >$1< >$2<"
    eval tmpVarName=\$$extVarName
    #echo "debug Ind Output >$tmpVarName< >$extVarName<"
    export $locVarName="${tmpVarName}"
}

I am currently using this little function. I am not fully happy with it, and I have seen different solutions on the web (if I could recall I would write them here), but it seems to work. Within these few lines there is already some redundancy and extra data but it was helpful for debugging.

If you want to see it in place, i.e. where I am using it, check: https://github.com/mariotti/bmu/blob/master/bin/backmeup.shellfunctions.sh

Of course it is not the best solution, but made me going on with the work, in the hope I can replace it with something a bit more general soon.

mariotti
  • 410
  • 1
  • 5
  • 23
  • Before questions ;) What I do not like of this function is that I use an "eval" which typically is a trigger for bad design. Of course there are places where you need it. What I like is that using a function I can detach the code from the problem and optimise it later. Maybe even introduce a shell version check or the like. – mariotti Mar 31 '17 at 16:13