1

I've been trying to do something for a couple of days, and I'm stumped; I keep running into the same problem, no matter how I approach this. I have a text file with 2 columns in it; the first is the variable name, the second is the command to be run, with the output being assigned to the variable in the first column. I use read to assign both columns to their own variables, then put the full expression into a new variable and execute it. No matter how I do it, I always get the expression as a command name and the error "command not found."

That's all a little convoluted, so let me show you. The script is:

while read varName varCmd
do
        echo varName is $varName
        echo varCmd is $varCmd
        declare cmd=$varName=$varCmd
        echo Command is $cmd
        "$cmd"
        echo 1st Value is $varFoo
        echo 2nd Value is $varBar

done < testvars.txt

And the text file is:

varFoo  echo foo
varBar  echo bar

Everything works except the assignment execution itself. Here's what I get:

varName is varFoo
varCmd is echo foo
Command is varFoo=echo foo
./testvars.sh: line 8: varFoo=echo foo: command not found
1st Value is
2nd Value is
varName is varBar
varCmd is echo bar
Command is varBar=echo bar
./testvars.sh: line 8: varBar=echo bar: command not found
1st Value is
2nd Value is

It looks like Bash is interpreting the whole thing as one command name (string) and not interpreting the = as an operator.

What can I do to get Bash to correctly interpret the assignment expression correctly?

7
  • That line number look wrong. Did you give us the same file that produced that error message? Commented Aug 24, 2021 at 22:06
  • @ctrl-alt-delor - Good catch! Yes, I did edit the script between running and posting it; I removed some extra lines that were still sitting around from previous attempts, and just confused the code. Those lines were not being used and did not effect the output beyond the line numbers. I'll have to make sure I make those edits before running next time, should I post again.
    – le_jawa
    Commented Aug 26, 2021 at 21:36
  • @AdminBee - Why did you feel compelled to edit my post; the parts you removed were not hurting anything. Adding the code box around "read" was a good idea though, I'll have to keep that in mind.
    – le_jawa
    Commented Aug 26, 2021 at 21:41
  • You are right, the introductory salutation did not "hurt". However there have been several discussions (e.g. here) on the question style for StackExchange sites which came to a consensus that questions should not include them. I never go as far as to edit a question only to remove greetings/introductory phrases, but since I thought the formatting edit would be worthwhile, I removed those parts "en passant". If you feel I have done your post unjustice, you are always free to revert edits, of course.
    – AdminBee
    Commented Aug 27, 2021 at 7:09
  • Adminbee - Thank you for the open and honest response; I greatly appreciate it. And I see where you're coming from now. Apparently, this is where Stack Exchange and I differ on things. The way I look at it, we're all human here, and we all like to be treated not only politely, but also kindly, or in a friendly manner. As a result, my posts take on a very conversational tone. Yeah, I realize that on technical forums, most of us are nerds, and as such, we tend to be very functional in our communication. That doesn't mean we can't also be a bit more casual and friendly with each other. :-)
    – le_jawa
    Commented Aug 27, 2021 at 14:18

3 Answers 3

5

This is similar to how you can't do this:

name=foo
"$name"=123

or this:

cmd="ls -l | grep ^d"
$cmd

Assignment words are recognized as such before variables are expanded, similarly to how other operators are parsed before variable expansion (and not recognized after that). That's also what other shells do, and what the standard says, so in a lot of senses the "correct" behaviour.

If you want to refer to a variable named in another, there's namerefs (from Bash 4.3), and also the declare command itself can do that. These would assign 123 to a variable called foo:

declare -n p=foo
p=123

or

varname=foo
declare "$varname=123"

However, it often makes more sense to use associative arrays:

key=foo
value=123
declare -A aa
aa["$key"]="$value"

and in case you're storing commands, see

1
  • ikkachu - Thank you very much for the thorough explanation and the references. Your response helped me better understand some additional things about bash, associative arrays in particular.
    – le_jawa
    Commented Aug 27, 2021 at 14:11
1

Yes use eval, but you must cause the command string to be executed.

eval $varName=$($varCmd)

Or you could ...

declare cmd="$varName=$($varCmd)"
eval $cmd
1
  • Thank you @stevea, that was it! Now I just need to decide if I want to execute the whole string as a command, or put the command in the script and pass it a parameter string from the text file.
    – le_jawa
    Commented Aug 27, 2021 at 14:06
-2

Use eval, described in man bash.

 eval $varBar
2
  • 2
    If the commands read from the file is only ever so slightly more complex, this may break or generate unexpected results. You need to quote $varBar as well as make sure that the code read from the file is valid and correctly quoted shell code. Try with the command cd /; printf '*\t%s\n' * in the input data.
    – Kusalananda
    Commented Aug 24, 2021 at 21:25
  • If you're going to recommend eval, you need to go into the pitfalls and dangers of doing such (some of which @Kusalananda pointed out). eval has its place, but it's a very precarious one.
    – Doktor J
    Commented Sep 8, 2021 at 19:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .