I would like to use some kind of minimalistic template engine with pure bash and envsubst
:
user@host:~$ env -i FOO=foo BAR="bar baz" envsubst '$FOO,$BAR' \
<<< 'Hello "$FOO" and "$BAR"!'
Hello "foo" and "bar baz"!
Above works, but only contains static variables.
Now let's assume environment variables are given dynamically, like with an associative array:
declare -A MY_ENV=([FOO]=foo [BAR]="bar baz")
Parsing the array key-value pairs only works for environment values without whitespaces (errors):
env -i \
$(for k in "${!MY_ENV[@]}"; do printf "%s=%s " $k "${MY_ENV[$k]}"; done) \
envsubst #...
Trying to wrap environment values by quotes (note '%s'
instead of %s
) also errors:
env -i \
$(for k in "${!MY_ENV[@]}"; do printf "%s='%s' " $k "${MY_ENV[$k]}"; done) \
envsubst #...
Output of set -x
:
(削除) Reason: :set -x
shows that the argument for env
becomes one giant string (削除ここまで)
+ env -i 'FOO='\''foo'\''' 'BAR='\''bar' 'baz'\''' envsubst #...
env: ‘baz'’: No such file or directory
I must have missed a shell escape lession (again...). How might I rewrite last example to work properly?
2 Answers 2
This is [BashFAQ/050] -- you have to use an array to ensure each "key=value"
pair is properly quoted.
vars=()
for k in "${!MY_ENV[@]}"; do
vars+=( "$k=${MY_ENV[$k]}" )
done
env -i "${vars[@]}" envsubst '$FOO,$BAR' <<< 'Hello "$FOO" and "$BAR"!'
Hello "foo" and "bar baz"!
Notice how I'm not injecting any extra quote characters.
Extending to so we don't have to hard-code the variable names:
keys=( "${!MY_ENV[@]}" )
printf -v varnames ',%s' "${keys[@]/#/'$'}"
env -i "${vars[@]}" envsubst "${varnames#,}" <<< 'Hello "$FOO" and "$BAR"!'
# or without the `varnames` temp var
env -i "${vars[@]}" envsubst "$(IFS=,; echo "${keys[*]/#/'$'}")" <<< 'Hello "$FOO" and "$BAR"!'
-
1Fantastic! Learned a lot, thank you. After reading Shell parameter expansion, solution became clear.user00705358365913– user007053583659132022年07月22日 20:20:47 +00:00Commented Jul 22, 2022 at 20:20
A workaround is to export
each key-value pair one-by-one and execute envsubst
afterwards:
for k in "${!MY_ENV[@]}"; do export $k="${MY_ENV[$k]}"; done
envsubst '$FOO,$BAR' <<< 'Hello "$FOO" and "$BAR"!'
#Hello "foo" and "bar baz"!
The code might be placed inside a subshell to prevent polluting the current environment.
(PS: Still curious, why dynamically building arguments after env
in original post did not work - any help appreciated.)
You must log in to answer this question.
Explore related questions
See similar questions with these tags.