Unix & Linux Asked by Swaroop Joshi on November 28, 2021
Say:
variable="Something that it holds"
then
echo "$variable"
will output: Something that it holds
But say I also do:
var2="variable";
echo "$$(echo $var2)"
will just output: $variable
And not: Something that it holds
Can anyone tell me about what feature of Unix is in play, here?
In addition to indirect variable expansion, in bash (starting from version 4.3) you can use a nameref
declare -n var2="variable" # "var2" is a _reference_ to "variable"
variable="Something"
echo "$var2" # => Something
variable="something else"
echo "$var2" # => something else
unset variable
echo "$var2" # => ""
I don't know how this is implemented, but this is an interesting tidbit: you can find out what a nameref refers to using indirection:
echo ${!var2} # => variable
Answered by glenn jackman on November 28, 2021
What you are observing is the standard behavior of a POSIX shell: in general, it
reads its input;
breaks the input into tokens: words and operators (token recognition);
$
(or `
), it recursively determines the type of expansion and the token to be expanded, reading all the needed input;parses the input into simple commands and compound commands;
performs various expansions (separately) on different parts of each command, resulting in a list of pathnames and fields to be treated as a command and arguments;
performs redirection and removes redirection operators and their operands from the parameter list;
executes a function, built-in, executable file, or script;
optionally waits for the command to complete and collects the exit status.
When parsing echo "$$(echo $var2)"
, the shell detects two expansions (step 2): the double-quoted command substitution $(echo $var2)
and the unquoted parameter expansion $var2
. The escaped $
in $
is taken as a literal dollar sign because a double-quoted retains its role as an escape character when followed by
$
.
No further detection of expansions happens at later stages. Specifically, there is no further parsing of the result of expansions performed in step 4 ("$$(echo $var2)"
→ "$$(echo variable)"
→ "$variable"
→ $variable
) that could detect expansion-triggering characters.
Also note that, while the $
symbol is used to replace the name of a variable with its content in the context of parameter expansion, it has not been designed as a general dereference operator.
In standard parameter expansion, whose simplest form is ${parameter}
, the parameter
specification is only allowed to be a variable name, a positional parameter or a special parameter (see the definition of "parameter"). Strictly speaking, parameter expansions can not be nested (an expansion expression is only allowed as word
in the various ${parameter<symbols>[word]}
forms).
You can easily verify that, with the exception of the Z shell, ${${foo}}
is not a valid expression and that $$
expands to the shell's PID (thus, $foo
expands to the value of foo
, $$foo
expands to the concatenation of the shell's PID and the literal "foo", $$$foo
expands to the concatenation of the shell's PID and the value of foo
, ...).
Answered by fra-san on November 28, 2021
variable="Something" var2="variable"; echo "$$(echo $var2)"
In the last line, you expect "$$(echo $var2)"
→ $variable
→ Something
.
This would require the shell to perform two parameter expansions. It does not, but rather simply prints the result of the command substitution $(echo $var2)
prepended by a $
.
In principle, eval helps you to get what you want. After the shell performs the first step, "$$(echo $var2)"
→ $variable
, eval performs the second step, $variable
→ Something
.
$ eval echo "$$(echo $var2)"
Something
Although in our particular case the above command is OK, that still lacks a correct quoting, and printf
is to be favored over echo
,
$ eval 'printf "%sn" "${'"$var2"'}"'
Something
However, eval raises security concerns with untrusted data. Suppose
var2="variable;rm importantFile"
. In that case, eval passes
echo $variable;rm importantFile
to the shell, which happily removes importantFile
, if it exists.
In some shells (e.g.: Bash, ksh, Zsh) you can also do it using indirection. The syntax of indirect expansion in Bash is:
$ echo "${!var2}"
Something
var2="variable;rm importantFile"
is not a problem anymore,
but var2='a[$(rm importantFile)]'
still is.
Read more about indirection in the Bash manual.
If the first character of parameter is an exclamation point (!), and parameter is not a nameref, it introduces a level of indirection. Bash uses the value formed by expanding the rest of parameter as the new parameter; this is then expanded and that value is used in the rest of the expansion, rather than the expansion of the original parameter
Answered by Quasímodo on November 28, 2021
Smells like an anti-pattern(*). Many shells have string-indexed arrays/dictionaries. In ksh93
(where that syntax comes from), bash
or zsh
:
typeset -A dictionary
x="keyX"
y="keyY"
dictionary[keyX]="valueX"
dictionary[$y]="valueY"
printf '%sn' "${dictionary[$x]}"
printf '%sn' "${dictionary[keyY]}"
(*) Nothing to do with Linux per se. The variable-name-in-a-variable is a very general "anti-pattern", something that shouldn't be done, and if you think you need it, there is a problem with your design (XY problem)? Most uses of a variable-name-in-a-variable are better replaced with a dictionary when that exists.
Answered by xenoid on November 28, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP