0

In Bash, I want to:

  1. Create 3 variables with name ID1, ID2, ID3
  2. Variables will be assigned string values ID1_VALUE, ID2_VALUE and ID3_VALUE respectively

I created a for loop like this

for ID_COUNT in 1 2 3; do
    ID${ID_COUNT}=ID${ID_COUNT}_VALUE;
done;

When the snippet is run, I got this

ID1=ID1_VALUE: command not found
ID2=ID2_VALUE: command not found
ID3=ID3_VALUE: command not found

and the value ID1, ID2, ID3 are all not set (set | grep ID1 shows nothing).

Could anyone please explain in details what happened?

  • 1
    You need do export ID${ID_COUNT}=ID${ID_COUNT}_VALUE; – Nasir Riley Dec 8 '18 at 4:43
  • Thanks for your response. But can you explain why I need to do that? I thought the export command is used for making a variable in a the current shell available in its sub-shell. This for loop doesn't run in a sub shell. Even if it does, export wouldn't make it visible in the parent shell (which is the current shell). – Tran Triet Dec 8 '18 at 4:51
  • The export command is used to set a variable. Try this for example: export ID1=ID1_VALUE and then echo $ID1 and you'll get ID1_VALUE as the output. You are getting an error because you are telling to run ID1=ID1_VALUE (as well as the others) as a command after do which doesn't work because those aren't commands. With export in place, there is a command to be executed and the value will be assigned to the variable which is what you want. – Nasir Riley Dec 8 '18 at 5:02
  • Thank you for explaining the purpose of the export command. It makes more sense now. However, you said running ID1=ID1_VALUE as a command after do doesn't work because it isn't a command. I tried an example: for A in 1 2 3; do B=$A; done; and then echo $B gives 3. I don't understand the difference between the 2 scripts. I notice that the only difference is the parameter expansion on the left in the assignment ID${ID_COUNT} and it is causing the problem. Can you please explain why? – Tran Triet Dec 8 '18 at 5:15
  • In your second example, you are assigning the value of $A to B. In your question, it is being interpreted as one long command because that isn't being done. Try using export and you'll see the difference. – Nasir Riley Dec 8 '18 at 5:34
2

Apparently bash is particular about the interpretation of a command line, where it discovers variable assignments before evaluating used variables. Basically a variable assignment requires the name part to be a proper name, and not one derived by evaluation. But there is the notion of "nameref", where a variable has a variable name as a value, and then the assignment to it instead becomes an assignment to the variable named by its value. It would look something like the following in your example:

for ID_COUNT in 1 2 3; do
    declare -n X=ID${ID_COUNT}
    X=ID${ID_COUNT}_VALUE;
done;

The same thing is of course achieved with the eval command, which evaluates its arguments as a command within its calling context. Using that would look as follows:

for ID_COUNT in 1 2 3; do
    eval ID${ID_COUNT}=ID${ID_COUNT}_VALUE;
done;

As noted, using export has the similar effect, with the addition of making the variable exported to sub processes, which is its primary function.

  • 1
    declare $x=y would be another option preferable to export, and also to eval if there's any doubt about the values involved. – Michael Homer Dec 8 '18 at 7:46
2

Your scripts, current and future, would be better if you used an array for this:

#!/bin/bash

for i in 1 2 3; do
    id[i]="ID${i}_VALUE"
done

echo 'contents of the array "id":'
printf '\t%s\n' "${id[@]}"

The output of this script would be

contents of the array "id":
        ID1_VALUE
        ID2_VALUE
        ID3_VALUE

The loop in the script could also be replaced by

id=( "ID"{1..3}"_VALUE" )

which uses a brace expansion to create the array.


What happens in your code is that the variable $ID_COUNT in ID${ID_COUNT}=ID${ID_COUNT}_VALUE; is expanded yielding ID2=ID2_VALUE if its value is 2. At that point in executing the command, however, variables have already been detected, so what you have now is a string with an equals sign in it. The shell will go on to try to execute this string as if it was the name of a command.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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