Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Follow-up to the background part in this question.

In bash I can use {!FOO} for double substitution, in zsh ${(P)FOO}. In both, the old-school (hack-y) eval \$$FOO works.

So, the smartest and most logical thing for me would be ${${FOO}}, ${${${FOO}}}… for double/triple/n substitution. Why doesn’t this work as expected?

Second: What does the \ do in the eval statement? I reckon it’s an escape, making something like eval \$$$FOO impossible. How to do a triple/n substitution with that that works in every shell?

share|improve this question

4 Answers 4

up vote 3 down vote accepted

The \ must be used to prevent the expansion of $$ (current process id). For triple substitution, you need double eval, so also more escapes to avoid the unwanted expansions in each eval:

#! /bin/bash
l0=value
l1=l0
l2=l1
l3=l2
l4=l3

echo $l0
eval echo \$$l1
eval eval echo \\$\$$l2
eval eval eval echo \\\\$\\$\$$l3
eval eval eval eval echo \\\\\\\\$\\\\$\\$\$$l4
share|improve this answer
    
Yet: l3=l2; eval eval eval echo \\\$\\$\$$l353294 so not exactly modular. –  Profpatsch Mar 15 '13 at 11:29
    
Why can’t I do something like eval $(eval echo \$$l2)? I hate bash, it makes absolutely no sense. –  Profpatsch Mar 15 '13 at 11:31
    
@Profpatsch: Added more examples. –  choroba Mar 15 '13 at 11:38
    
Ah, it’s n². I don’t even want to try to get why it’s like this. –  Profpatsch Mar 15 '13 at 11:43
1  
@Profpatsch: Easy. In each step, the number of backslashes halves (so it is 2ⁿ, in fact). –  choroba Mar 15 '13 at 11:46

Supposing the value of FOO is a valid variable name (say BAR), eval \$$FOO splits the value of BAR into separate words, treats each word as a wildcard pattern, and executes the first word of the result as a command, passing the other words as arguments. The backslash in front of the dollar makes it be treated literally, so the argument passed to the eval builtin is the four-character string $BAR.

${${FOO}} is a syntax error. It doesn't do a “double substitution” because there's no such feature in any of the common shells (not with this syntax anyway). In zsh, ${${FOO}} is valid and is a double substitution, but it behaves differently from what you'd like: it performs two successive transformations on the value of FOO, both of which are the identity transformation, so it's just a fancy way of writing ${FOO}.

If you want to treat the value of a variable as a variable, be careful of quoting things properly. It's a lot easier if you set the result to a variable:

eval "value=\${$FOO}"
share|improve this answer
1  
Setting it as variable, so that the first word isn’t used as command? Man, this sort of logic is hard to grasp. It’s the general problem I seem to have with bash. –  Profpatsch Mar 19 '13 at 13:45

Just like ${$foo} doesn't work in place of ${(P)foo} in zsh, neither does ${${$foo}}. You just need to specify each level of indirection:

$ foo=bar
$ bar=baz
$ baz=3
$ echo $foo
bar
$ echo ${(P)foo}
baz
$ echo ${(P)${(P)foo}}
3

Of course, ${!${!foo}} doesn't work in bash, because bash doesn't allow nested substitutions.

share|improve this answer
    
Thanks, that’s good to know. A shame that this is zsh-only, so you can’t really put it in a script (everyone has bash, but it’s not the other way around). –  Profpatsch Mar 24 '13 at 16:08
    
I suspect zsh is installed far more often than you might assume. It may not be linked to sh like bash often (but not always) is, but it may still be available for shell scripts. Think of it like you do Perl or Python. –  chepner Mar 24 '13 at 16:24

Why would you need to do that?

You can always do it in several steps like:

eval "l1=\${$var}"
eval "l2=\${$l1}"
...

Or use a function like:

deref() {
  if [ "$1" -le 0 ]; then
    eval "$3=\$2"
  else
    eval "deref $(($1 - 1)) \"\${$2}\" \"\$3\""
  fi
}

Then:

$ a=b b=c c=d d=e e=blah
$ deref 3 a res; echo "$res"
d
$ deref 5 a res; echo "$res"
blah

FWIW, in zsh:

$ echo ${(P)${(P)${(P)${(P)a}}}}
blah
share|improve this answer
    
Huh, using several steps didn’t occur to me until now… strange. –  Profpatsch Mar 24 '13 at 18:08
    
It's obvious, from my point of view, why @Profpatsch wants to do that. Because it is the most intuitive way to do it. Being hard to implement multiple substitutions in bash is another thing. –  Nikos Alexandris Jul 26 '13 at 15:35

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.