zsh: How to get value of variable based on a another variable?
With zsh
:
${(P)varname}
expands to the value of the variable whose name is stored in$varname
. So if$varname
containsvar
,${(P)varname}
expands to the same thing as$var
.${(e)var}
expands to the content of$var
but also performs parameter expansions, command substitution and arithmetic expansion within. So if$var
contains$othervar
,${(e)var}
expands to the same thing as$othervar
.- You can nest variable expansion operators, so things like
${(P)${var:-something}}
work ${:-content}
is one way to have a parameter expansion expand to arbitrary text (herecontent
)
(see the manual for details)
So you could do:
_v1=windows
_v2_windows=/mnt/d
printf '%s\n' ${(P)${:-_v2_$_v1}}
Or:
printf '%s\n' ${(e)${:-\$_v2_$_v1}}
Or do it in two steps:
varname=_v2_$_v1
printf '%s\n' ${(P)varname}
Or:
expansions_to_evaluate=\$_v2_$_v1
printf '%s\n' ${(e)expansions_to_evaluate}
Or you could use the standard POSIX syntax:
eval 'printf "%s\n" "${_v2_'"$_v1"'}"'
Beware that if the value of $_v1
is not under your control, all those amount to an arbitrary command injection vulnerability, you'd need to sanitize the value first.
Also note that zsh
supports associative arrays (and has long before bash
), so you can do:
typeset -A mnt
mnt=(
windows /mnt/d
osx /Volumes/d
)
os=windows
printf '%s\n' $mnt[$os]
Which would be a lot more legible, and wouldn't have any of those security implications.
printf '%s\n' "${(P)$(echo "_v2_$_v1")}"
alternatively
var=_v2_$_v1
printf '%s\n' "${(P)var}"
In both cases, the parameter expansion flag (P)
is used to expand the name inside ${...}
into the name of the actual variable that we'd like to get the value of.
This is similar to variable indirection with ${!...}
in the bash
shell.