0

I am trying to create a Variable from value of another variable and trying to store value into the created variable in Shell Script. For Example: Let there is a existing variable i=1

I am trying to store value in var_$i

I have tried:

i=1
"var_$i"='true'
echo "$var_1"

Output:

bash: var_1=true: command not found`

AND

i=1
var_indirect="var_$i"
${!var_indirect}='true'
echo "$var_1"

Output:

bash: =true: command not found

I am expecting that echo $var_1 will give the output true.

4
  • The declare builtin might help, I can't find the link now but try: declare i=1 var_$i='true'; echo "$var_1"
    – Jetchisel
    Commented Jul 6 at 1:28
  • 2
    See shellcheck.net for validation/recommendation
    – Jetchisel
    Commented Jul 6 at 1:37
  • 4
    Associative arrays are the way to go instead of trying to make a new variable name at runtime.
    – Shawn
    Commented Jul 6 at 2:11
  • 2
    If $i is always numeric, you don't even need an an associative array, just a plain numeric-indexed one. i=1; var[i]='true'; echo "${var[1]}". In any case, dynamic variable names are a mess, and should be avoided if at all possible (usually by using some sort of array instead). Commented Jul 6 at 5:51

2 Answers 2

0

We are going to use eval. For the least amount of risk, we prepare the value to be stored into the computed variable in a variable called val

The idiom is:

eval var_$i=\$val

The argument to eval undergoes substitution, producing the syntax var_1=$val. The dollar sign is escaped, and so the escape is removed and the dollar sign is preserved.

This var_1=$val syntax is evaluated by eval, resulting in the desired assignment.

Note that A=$B assignments do not require quoting.

If a malicious user gains control over the $i variable, they can fool our eval into doing bad things. For instance, suppose i contains this text:

foo=bar; rm -rf ~/*; bar

then eval will be invoked with the argument:

var_foo=bar; rm -rf ~/*; bar=$val

a syntactically correct command sequence consisting of two assignments sandwiching a recursive removal. So you have to be very careful about how i acquires its value.

Bash has indirect variables and namerefs (by imitation of similar features found in the Korn shell).

Quick demo: indirect vars:

$ var=TERM
$ echo ${!var}
screen-256color

namerefs:

$ declare -n var=TERM
$ echo $var
screen-256color
$ var=ansi
$ echo $var
ansi
$ echo $TERM
ansi

Using a nameref we can create an alias for var_$i and set it that way:

$ i=42
$ declare -n ref=var_$i
$ ref=content
$ echo $var_42
content

This is safer than using eval, and probably more performant; just not portable. The eval method uses only POSIX shell features.

0

In your code:

i=1
"var_$i"='true'
echo "$var_1"
  • "var_$i"='true' is checked for "starts with valid variable name followed by equal character" before expansion of $i or removal of quotes is attempted, but "var_$i" is not a valid variable name so no assignment is performed

As the comments note, using declare can fix this.

i=1
declare "var_$i"=true
echo "$var_1"

Your second attempt fails for similar reason.

i=1
var_indirect="var_$i"
${!var_indirect}='true'
echo "$var_1"
  • ${!var_indirect} is not a valid variable name, so this is not treated as assignment
  • also ${!var_indirect} gives a value, not a variable name

Similar fix is possible:

i=1
var_indirect=var_$i
declare "$var_indirect"=true
echo "$var_1" "${!var_indirect}"

From bash man-page:

Simple Commands
    A  simple  command  is a sequence of optional variable assignments fol‐
    lowed by blank-separated words and redirections, and  terminated  by  a
    control operator.  The first word specifies the command to be executed,
    and is passed as argument zero.  The remaining words are passed as  ar‐
    guments to the invoked command.
  • i=1 j=2 echo blah is 2 variable assignments followed by 2 words
  • i=1 "k"=v echo blah is 1 assignment followed by 3 words
  • declare "$k"="$v" is 2 words

Using set -k changes this behaviour.

2
  • The question had formatting issues which are now fixed; the commands weren't all on one line.
    – tripleee
    Commented Jul 6 at 6:06
  • @tripleee oops, I should have checked that
    – jhnc
    Commented Jul 6 at 6:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.