You can do this, but it requires at least 2 shell variable evaluations, and, normally, all you ever get is one. This is by design - I mean, the shell is where all of your stuff is, so it's best to limit the unexpected to a minimum and avoid opening doors behind doors if you can help it. In any case, you definitely have at your disposal the eval
shell built-in:
% one=two ; two=three ; eval echo \$$one
> three
% eval "one=\$$two" ; echo $one
> three
It does not store a one
-> two
pointer, but rather it evaluates its arguments as a single shell statement. If that's a little unclear to you, you're not alone. It basically means that the shell runs the same line twice, or at least as near as I can figure it does, and all of the second pass's assignments are affected by the first pass's expansions - which is not normally the case, as you demonstrate in your question. It also means that two
's literal value from a certain point in time is stored in one
- copied, not linked, so:
% one=two ; two=three
% eval echo \$$one ; two=four ; eval echo \$$one
> three
> three
% echo $two
> four
But what's up with the backslash? Well, this is where it gets confusing - because you're making two passes you have to quote your quotes. The first pass expands $one
to two
and strips the first level of quotes, so bye-bye \backslash, but the quoted dollar-sign remains and so $two
becomes three
on the second pass. This gets really crazy really fast.
Shell quotes are hard enough to deal with as it is - just try to quote a single quote. Layer them and you're asking for trouble, strip, evaluate, strip, evaluate and you're going nowhere safe at all - because quotes are there to protect the values of shell parameters and environment variables and the shell from their values.
So what you've done is store a literal $
dollar sign within the value of your variable. You didn't store a path to $HOME
because the shell did not expand its value during assignment - again, by design.
% dir=some_dir ; eval "$dir=\$HOME" ; echo $some_dir
> /your/$HOME/path/
You don't have to ask around much about this stuff before you hear of the evil eval
. It's commonly called that, and for good reason. Look:
two=var\ \;rm\ -f\ /all/your/stuff ; eval home=$two
Above home is assigned to the expansion of $two. The home=
portion of the statement is evaluated twice - after the first pass and before the second, home briefly equals "$two" ; but upon the second, after the quotes are stripped, $home
expands to var
and /all/your/stuff
equals 0. That's a super basic quoting example as well, it can go really wrong in a more complicated situation - and just requirement of eval
tends to denote complicated.
There are other ways to accomplish this - GNOUC notes the ${!
bash specific}
value inversion assignment (I think that's what it's called...) which probably just does the same thing but in a way that bash
has attempted to safeguard. That's a good thing. You could also - and this is probably a good idea - write a function to spit out the values you want when it's called:
_home() { exec 2>/dev/null ; set -- /home/*/ ; for h do (
cd "$h" && cd "$dir" && printf %s\\n "$dir" )
done
} ; var="$(_home)" ; echo "$var"
> probably...
> the...
> result...
> you...
> wanted...
Almost as dangerous as eval
can be a little imagination in combination with use of the shell's .
as well. Because .
's job is to execute commands in the current shell environment rather than subshelling out as do pipes and etc, it's possible to construct a twice-evaluated statement that can affect your environment variables with little fuss.
For instance, if you do not quote the limiter on a here-document
its contents are subject to shell expansion. If you take that one further and .
source it you can execute the results of its expansions. Like this:
% . <<HERE /dev/stdin
> ${one=two}three=four
> home='$HOME'
> HERE
% [ "$home" = "$HOME" ] && echo $twothree
> four
See, in this way, at least, the quotes are easier because they're not stripped except from within an expansion, but they can still get tricky when your expansions do contain quotes. Anyway, that about wraps it up for what I know on the subject.
Actually, one more thing, and this is super-duper risky, but it's your computer:
% var=eval\ echo\ \$HOME
% $var
> /YOUR/HOME
But...
% cd eval echo $HOME
obviously doesn't work...
export TEST="$HOME/dir"
– devnull Mar 15 '14 at 11:11