17

I'm trying to have two layers of indirection, though let me know if I'm a victim of the XY problem

Boiled Down Explanation of What I'd Like to Achieve

> test1=\$test2
> test2="string to print"
> echo $test2
string to print
> echo $test1
$test2

This all makes sense, but what I want is to perform a command using $test1 and print out string to print. My gut reaction was that this should work

> echo $(echo $test1)
$test2

Bollocks. Does anyone else know if this is possible?

More Detailed Explanation of Why I Wish to Accomplish This

I want to create a text file template containing $variables which can be re-used to generate many text files. My thinking is: set environment variables, process the template file and output to another file.

Example:

> #set vars
> cat template.txt | magic_var_expansion_cmd > out1.txt
> #set vars
> cat template.txt | magic_var_expansion_cmd > out2.txt
> #set vars
> cat template.txt | magic_var_expansion_cmd > out3.txt

This could obviously be used in a script, and in more sophisticated ways but this is the MVP in my mind's eye.

  • You can use parameter expansion for this: echo ${test1@P}. See this answer: https://unix.stackexchange.com/a/731950/133046 – Alek Jan 16 '23 at 11:18

2 Answers2

16
$ test1="hello"
$ test2="test1"

$ echo "${!test2}"
hello

From the bash manual:

If the first character of parameter is an exclamation point (!), and parameter is not a nameref, it introduces a level of variable indirection. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion. If parameter is a nameref, this expands to the name of the variable referenced by parameter instead of performing the complete indirect expansion. The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below. The exclamation point must immediately follow the left brace in order to introduce indirection.

For the second part of the question, I would probably try to avoid using actual shell variables in the template, and instead use easy to parse placeholders and replace these using a tool like sed.

There are a few similar questions around, including "How to replace placeholder strings in document with contents from a file", "Tool to create text files from a template" and "How to replace a list of placeholders in a text file?" (and there are others too).

Kusalananda
  • 333,661
  • This is really close to what I was looking for, with one wrinkle. If it were test2=test1 hello, then echo "${!test2}" doesn't print "hello hello", it prints nothing. – Frank Bryce Jan 30 '17 at 17:25
  • 3
    If the string contains variables, but isn't exculsively itself the name of a variable, is this possible? – Frank Bryce Jan 30 '17 at 17:27
  • what about something more complicated? #export RUN_CMD=$(echo $('$HOME/diversity-for-predictive-success-of-meta-learning/div_src/diversity_src/experiment_mains/main_diversity_with_task2vec.py --manual_loads_name diversity_ala_task2vec_delauny > $OUT_FILE 2> $ERR_FILE')) – Charlie Parker Nov 23 '22 at 20:38
  • @CharlieParker I don't really see how this is related. If you want to run a command in a variable, you should most likely make that variable an array: runcmd=( "$HOME"/somepath/script.py --options here ); "${runcmd[@]}" >out 2>err. – Kusalananda Nov 23 '22 at 20:54
  • @Kusalananda it's complicated to explain, but I just need to create the string I need (but happy to explain). – Charlie Parker Nov 23 '22 at 21:02
  • @CharlieParker You would best do that in a brand new question. That way other people may get a chance to give better answers than I could ever give in a comment. – Kusalananda Nov 23 '22 at 21:06
  • @Kusalananda here it is: https://unix.stackexchange.com/questions/726079/how-does-one-store-the-evaluation-of-a-big-string-with-multiple-env-variables-v – Charlie Parker Nov 23 '22 at 21:13
9

use eval

$ test1=\$test2
$ test2="string to print"
$ echo $test2
string to print
$ eval echo $test1
string to print
Ipor Sircer
  • 14,546
  • 1
  • 27
  • 39
  • 1
    This is also useful for "deliteralizing" strings with special characters like tildes or curly braces so they can be passed around in a variable for later expansion. – Thomas G Henry LLC Mar 06 '20 at 21:11
  • how do I store it in a variable? e.g. export RUN_CMD=(eval echo '$HOME/diversity-for-predictive-success-of-meta-learning/div_src/diversity_src/experiment_mains/main_diversity_with_task2vec.py --manual_loads_name diversity_ala_task2vec_delauny > $OUT_FILE 2> $ERR_FILE') – Charlie Parker Nov 23 '22 at 20:39