Dynamic variable name in bash

Hello,

I'm trying to build a small script to store a command into a dynamic variable, and I have trouble assigning the variable.

#!/bin/bash

declare -a var1array=("value1" "value2" "value3")
var1arraylength=${#var1array[@]}

for (( i=1; i<${var1arraylength}+1; i++ ));
do
     mkdir "${var1array[$i-1]}"

     var2=${var1array[$i-1]}
     
     "$var2Value01=$(command 01)"
     "$var2Value02=$(command 02)"

     if [ ! -z $var2Value01] ; then
          echo $var2Value01 > file-$var2Value01.err
     else
          echo "No errors in $var2Value01"
     fi

     if [ ! -z $var2Value01] ; then
          echo $var2Value01 > file-$var2Value02.err
     else
          echo "No errors in $var2Value01"
     fi


done

I imagine the problem with this script is when assigning the dynamic variables

"$var2Value01=$(command 01)"

and

"$var2Value02=$(command 02)"

Does anyone had similar issues, and if yes how can this be solved?
I've tried with

 eval "$var2Value01=$(command 01)"

but without any luck.

This is correct, but maybe in a different way than you may have thought.

The shell parses the commandline in several distinct steps and one of these steps is the "expansion" of variables - that is, replacing them with their values. The problem is that all variables are expanded at the same time.

Therefore you need eval , because your construct implies a "second pass of expansion" to be applied onto your line. Unfortunately you cannot selectively restart a certain parsing step. You can only restart the whole process from the beginning. Therefore you perhaps need - to make your line work - to distinguish between the parts that are meant to be expanded in the first pass and the ones meant to be expanded in the second one. Notice that the shell "consumes" some characters and if you need to have them still there you need to escape them properly so that they are not consumed in the first pass:

$ echo "two words"
two words
$ echo \"two words\"
"two words"

$ eval echo "two words"
two words
$ eval echo \"two words\"
two words
$ eval echo \\"two words\\"
two words
$ eval echo \\\"two words\\\"
"two words"

To really understand all the differences between these lines pipe the echo-output into wc -w to count the words for every command variation.

A word about eval in general: stay away from it if you can! Most times you can find a solution where you do it differently and you don't need eval at all. This is (in almost every case) preferable, because eval is very dangerous. It is very easy to lose track of what really gets executed and why. Only if there is definitely no other way then use it as a last resort.

I hope this helps.

bakunin

2 Likes

In addition to what bakunin has already said, please consider the following...

I am having difficulty understanding what you are trying to do. The shell command language defined by the POSIX standards does not provide for dynamic variables in the shell command language. And bash , ksh , and most other shells I normally use do not support dynamic variables even as an extension to the standards. And, I don't see anything in your code that shows any need for dynamic variables.

What are you attempting to do with the following commands:

     "$var2Value01=$(command 01)"
     "$var2Value02=$(command 02)"

? The variables var2Value01 and var2Value02 have not been defined in this script, so the strings on the left side of the = will expand to empty strings (unless these variables have been assigned values in environment supplied by whatever invoked this script). Furthermore, since the = is quoted in both of these statements, these are not assignment statements; they are attempts to invoke a command named by the entirety of that quoted string after variable substitution and command substitution have been performed.

The commands:

     if [ ! -z $var2Value01] ; then
and
     if [ ! -z $var2Value01] ; then

both contain syntax errors (there must be a break (normally a <space> character) before the closing square bracket ( ] ) in a test command. And, again, nothing in this script defines either of the variables that are being tested in these if statements.

1 Like

I can honestly say I have no idea what you are trying to do.
There are many errors:-

"$var2Value01=$(command 01)"
"$var2Value02=$(command 02)"

Should be:-

var2Value01=$(command 01)
var2Value02=$(command 02)

Should the "Value01" and "Value02" parts read the same as your array values?
Where is var2 used?
The square brackets "]" should have a space before them " ]".
Your "if then else" conditions make no sense as they are both identical except for the one line, echo $var2Value01 > file-$var2Value02.err . This would end up with two identical error files.

1 Like

Hello,

Basically the above script supposed to be a short version of:

#!/bin/bash

     mkdir value1
     mkdir value2
     mkdir value3
     
     value1Value01=$(command 01 value1)
     value1Value02=$(command 02 value1)

     value2Value01=$(command 01 value2)
     value2Value02=$(command 02 value2)
     
     value3Value01=$(command 01 value3)
     value3Value02=$(command 02 value3)
     
     if [ ! -z value1Value01 ] ; then
          echo value1Value01 > file-value1Value01.err
     else
          echo "No errors in value1Value01"
     fi

     if [ ! -z value1Value02 ] ; then
          echo value1Value02 > file-value1Value02.err
     else
          echo "No errors in value1Value02"
     fi

     if [ ! -z value2Value01 ] ; then
          echo value2Value01 > file-value2Value01.err
     else
          echo "No errors in value2Value01"
     fi

     if [ ! -z value2Value02 ] ; then
          echo value2Value02 > file-value2Value02.err
     else
          echo "No errors in value2Value02"
     fi

     if [ ! -z value3Value01 ] ; then
          echo value3Value01 > file-value3Value01.err
     else
          echo "No errors in value3Value01"
     fi

     if [ ! -z value3Value02 ] ; then
          echo value3Value02 > file-value3Value02.err
     else
          echo "No errors in value3Value02"
     fi

done

I need to store the shell command results into a new variable and check if is empty,if not empty output to a file file-valueXValue0Y.err.

     
"$var2Value01=$(command 01)"
"$var2Value02=$(command 02)"

The output of the next shell commands

command 01

and

command 02 

supposed to be assigned to the above variables.

Don Cragun and wisecracker are correct regarding the test commands and I have adjusted them in the detailed code.

Hopefully this time, I managed to give a better picture of what I'm trying to achieve.

Using bash you can try:

declare ${var2}Value01=$(command 01)
declare ${var2}Value02=$(command 02)

or

printf -v ${var2}Value01 "%s" $(command 01)
printf -v ${var2}Value02 "%s" $(command 02)

These variables can then be referenced using bash indirect expansion like this:

varname1=${var2}Value01

if [ ! -z ${!varname1} ] ; then
     echo ${!varname1} > file-${!varname1}.err
else
     echo "No errors in $varname1"
fi
2 Likes

This is a demonstration longhand using 'eval' OSX 10.12.3, default bash terminal.
It shows how to create a new variable on the fly and how to extract its contents:-

Last login: Mon Feb 13 09:41:28 on ttys000
AMIGA:amiga~> var1="value1"
AMIGA:amiga~> eval "$var1"Value01=$( echo "MYVALUE1" )
AMIGA:amiga~> echo 'New _dynamic_ variable is '"$var1"'Value01...'
New _dynamic_ variable is value1Value01...
AMIGA:amiga~> echo "Transferred data to new variable is $value1Value01..."
Transferred data to new variable is MYVALUE1...
AMIGA:amiga~> _

Hope this is helpful as a starter...

Remember 'eval' is a _dangerous_ command to use...

1 Like

Gentleman, thank you so much for the time spend helping me to sort this issue out.

Based on your inputs, I managed to assign the variables correctly and have the script running.

Many Thanks