Bash – How to dynamically evaluate and set a variable

December 19, 2012 at 16:54

One of the things that opens the gate to truly code reusation in Bash is, in my opinion, the hability of evaluate and set variables in a dynamic way. By dynamic, I mean that the variable name is constructed dynamically, or passed as a parameter, and ends up being in another variable (for example, a variable local to a function). In fact, this is usually called indirect reference in Bash documentation.

Assigning a variable value

I usually have in my functions.sh file something like the following:

[22:57] $ cat functions.sh 
function execute_and_log {
	echo "Executing '$1' ..."
	export ${2}="$(eval $1)"
}
[22:57] ~ $ source functions.sh 
[22:57] ~ $ execute_and_log "ls /usr" LS_RESULT 
[22:57] ~ $ echo $LS_RESULT
Executing 'ls /usr' ...
X11/ X11R6@ bin/ include/ lib/ libexec/ llvm-gcc-4.2/ local/ sbin/ share/ standalone/ texbin@

As you can see, I pass the variable name where I want to store my result as a parameter to the function. The trick is to wrap the variable that contains the variable name with braces. Note that if you don’t use export (that in most cases is not needed), it won’t work, I’m not sure why. Another possibility is to use the allmighty eval, which gives you an unglier sintax, but allows you to avoid the export:

[22:59] $ cat functions.sh 
function execute_and_log {
	echo "Executing '$1' ..."
	eval "$2=\"$(eval $1)\""
}
[22:59] ~ $ source functions.sh 
[22:59] ~ $ execute_and_log "ls /usr" LS_RESULT 
[22:57] ~ $ echo $LS_RESULT
Executing 'ls /usr' ...
X11/ X11R6@ bin/ include/ lib/ libexec/ llvm-gcc-4.2/ local/ sbin/ share/ standalone/ texbin@

Evaluating a variable value

Imagine that in the previous example you don’t want to log only the command to execute, but also the result. You then need to evaluate the value of a variable whose name is contained in another variable (the function second param in this case). In this case, I only know how to do it using eval, which would give you the following:

[11:11] ~ $ cat functions.sh 
function execute_and_log {
	echo "Executing '$1' ..."
	eval "$2=\"$(eval $1)\""
	eval echo "Result is \$$2"
}
[11:11] ~ $ source functions.sh ; execute_and_log "ls /usr" LS_RESULT 
Executing 'ls /usr' ...
Result is X11/ X11R6@ bin/ include/ lib/ libexec/ llvm-gcc-4.2/ local/ sbin/ share/ standalone/ texbin@

As you can see, we can use eval both to assign and evaluate, in this second case just by escaping the dollar sign. Really powerful 🙂 .