3

I want to achieve the parameter substitution inside indirect expansion, but I cannot achieve it with a single command. I get a "bad substitution" error. Let me know if there's a clever way of solving this.

So I have to use the extra command to resolve the first variable value, then refer to it inside indirect expansion.

How can I do this in a single command without the extra command resolving the first variable value?

$ export first="hello"
$ export second_hello="finally"
$ echo ${second_${first}}
bash: ${second_${first}}: bad substitution 
$ echo ${!second_${first}}
bash: ${!second_${first}}: bad substitution
$ temp=second_${first}
$ echo ${!temp}
finally

I want to avoid the additional temp variable to get the string finally.

2 Answers 2

7

In bash, you'll need an intermediary variable:

first=hello
second_hello=finally
intermediary=second_$first
printf '%s\n' "${!intermediary}"

Though you can always do it the standard way using eval:

eval 'printf "%s\n" "${second_'"$first"'}"'

Where we pass printf "%s\n" "${second_hello}" to eval and eval in turn, interprets it as shell code which invokes printf with %s\n and finally as separate arguments. Note the quotes in both the outer and inner code to prevent split+glob.

In zsh where the equivalent of bash's ${!var}, is ${(P)var}, you can do without an intermediary variable by applying the P parameter expansion flag to a ${:-text} expansion:

first=hello
second_hello=finally
printf '%s\n' ${(P)${:-second_$first}}

Or using the e parameter expansion flag:

printf '%s\n' ${(e):-\$second_$first}

Note that all those approaches are equally safe if $first is guaranteed to contain only characters that are valid in a variable name and equally unsafe (introducing arbitrary command execution vulnerabilities) if not, so you may need to sanitise the value of $first if it's provided externally to your script.

Here, you may want to consider using associative arrays instead:

typeset -A second
second[hello]=finally
first=hello
printf '%s\n' "${second[$first]}"

Though beware you can't export an associative array variable as environment variables are only string/scalar. But it doesn't sound like you'd want any of those variables to be exported. You only want to export variables which you expect commands you execute to "import", like you export MANPATH for the man command to import it to know where to look for man pages.

0

I have found this without use of the intermediary variable.

[cloudshell-user@ip-10-0-25-104 ~]$ first=hello
[cloudshell-user@ip-10-0-25-104 ~]$ second_hello=finally
[cloudshell-user@ip-10-0-25-104 ~]$ eval echo \$second_$first
finally
1
  • maybe, but if hello contains e.g. a command substitution, the eval will evaluate it and execute the command inside, while the indirect expansion with the value passed through a temporary variable will just give an error. Also if you ever want to assign to a variable referenced like that, something like eval second_$first=... would need extra quoting on the right hand side too. Use a nameref or an associative array instead.
    – ilkkachu
    Oct 9, 2022 at 17:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .