If you have a bash variable that contains other variables, is it possible to "double" expand it as part of a command?
For example:
# Example #1
# ----------
# We define our first variable
TOP_LEVEL=/usr;
# Then use it (trivially) to set the values of other variables when executing a command
INC_DIRS="-I$TOP_LEVEL/include -I$TOP_LEVEL/local/include" \
LIB_DIRS="-L$TOP_LEVEL/lib -L$TOP_LEVEL/local/lib" \
./my_command
This is fairly self-explanatory. Now let's expand this example to the problem scenario:
# Example #2
# ----------
# We define our first variable
TOP_LEVEL=/usr;
# Notice the use of single quotes below, to defer expansion of `TOP_LEVEL` to some point
# later - the time when DEFERRED_EVAL_INC_DIRS will actually be used
DEFERRED_EVAL_INC_DIRS='-I$TOP_LEVEL/include -I$TOP_LEVEL/local/include'
... # Other stuff happens here that may change the value of TOP_LEVEL, e.g.:
TOP_LEVEL=/usr/confused
# Since we used single quotes to define DEFERRED_EVAL_INC_DIRS, it is not affected
# Now we want to execute a command like before, and pass it three variables,
# INC_DIRS, LIB_DIRS, and FOO_LIB_INC_DIRS, in their fully expanded form:
INC_DIRS="-I$TOP_LEVEL/include -I$TOP_LEVEL/local/include" \
LIB_DIRS="-L$TOP_LEVEL/lib -L$TOP_LEVEL/local/lib" \
FOO_LIB_INC_DIRS="$DEFERRED_EVAL_INC_DIRS" ./my_command
The expansion of INC_DIRS
and LIB_DIRS
is trivial, they become:
INC_DIRS='-I/usr/confused/include -I/usr/confused/local/include'
LIB_DIRS='-L/usr/confused/lib -I/usr/confused/local/lib'
The problematic expansion is with FOO_LIB_INC_DIRS
. The desired (fully expanded value should be):
FOO_LIB_INC_DIRS='-I/usr/confused/include -I/usr/confused/local/include'
..., finally expanding the embedded $TOP_LEVEL
variables used in the value of DEFERRED_EVAL_INC_DIRS
to their present value of /usr/confused
. So, there is a second evaluation pass needed for that variable and it has spaces in its value that need to be preserved as well, just to make life even more difficult.
Had DEFERRED_EVAL_INC_DIRS
referred to a single variable by name (and contained no other characters), the ${!DEFERRED_EVAL_INC_DIRS}
indirect variable reference construct could have been used. But, this is a more complicated scenario with surrounding text and multiple uses of the variable that needs to be expanded.
I tried playing with bash's eval
, but could not get this expansion to happen in one command line and without introducing additional variables, basically as an expansion of the code segment above. The full desired expanded result is:
INC_DIRS='-I/usr/confused/include -I/usr/confused/local/include' \
LIB_DIRS='-L/usr/confused/lib -I/usr/confused/local/lib' \
FOO_LIB_INC_DIRS='-I/usr/confused/include -I/usr/confused/local/include' \
./my_command
Is there a simple change or set of changes that can be made to the command invocation in Example #2 in order for the full expansion to occur?
Update: This is neither a duplicate (at least in any obvious way) of similar questions, due to the more complex deferred expansion involved, while preserving word boundaries across spaces. If the solution seems obvious to you, please post it as an answer and a change to:
INC_DIRS="-I$TOP_LEVEL/include -I$TOP_LEVEL/local/include" \
LIB_DIRS="-L$TOP_LEVEL/lib -L$TOP_LEVEL/local/lib" \
FOO_LIB_INC_DIRS="$DEFERRED_EVAL_INC_DIRS" ./my_command
..., to expand the variable DEFERRED_EVAL_INC_DIRS
fully, respecting word boundaries properly, and producing:
INC_DIRS='-I/usr/confused/include -I/usr/confused/local/include' \
LIB_DIRS='-L/usr/confused/lib -I/usr/confused/local/lib' \
FOO_LIB_INC_DIRS='-I/usr/confused/include -I/usr/confused/local/include' \
./my_command
..., preferably without creating a subshell if possible (and without breaking up this command into multiple commands or adding temporary variables, which would make this a much simpler case).