0

I made a bash script. It reads a file contains a list of commands and runs each of them.

A list of commands is like below.

ENV_NUMBER=1 command1
ENV_NUMBER=2 command2
ENV_NUMBER=3 command3

Each line has a environment variable before command to set same name but different value to commands.

A script is like below.

while read line
do
  if [ -n $line ] ; then
    # run command in background
    $line &
  fi
done < comandlist.txt

I expect:

  1. run command1 with ENV_NUMBER=1
  2. run command2 with ENV_NUMBER=2
  3. run command3 with ENV_NUMBER=3

But, I ran script then I got error:

ENV_NUMBER=1: command not found

How do I fix it?

4
  • Are you sure you are using bash? It looks right. Commented Mar 23, 2022 at 14:29
  • if [ -n $line ] will not behave as expected when $line is the empty string. In that case, the command expands to if [ -n ], and [ -n ] will succeed (return 0) because -n is not the empty string. You must use quotes here: if [ -n "$line" ] (or use some other test) Commented Mar 23, 2022 at 14:31
  • 1
    I prefer [[ ... ]] over [ ... ] in every case if it's available at all. It throws a syntax error with no arg. :) Commented Mar 23, 2022 at 14:56
  • Thank you all. I'll take both advices :-) Commented Mar 25, 2022 at 11:16

2 Answers 2

2

See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01 for a description of the order in which a simple command is parsed. Basically, in step 1, the line is examined for variable assignments. Since the simple command at this point is simply "$line", there are no variable assignments. Then, in step 2, $line is expanded to be "ENV_NUMBER=1 command1" and the first field is taken as the command. The line is not again scanned for variable assignments, and the string ENV_NUMBER=1 is taken as the command to be executed.

It sounds as if you want to evaluate the string "$line", in which case you would need to do eval "$line" rather than just executing $line. But beware the common knowledge "eval is evil". Using eval like this is generally considered bad practice.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for clear explanation. I understand what I actually did. But I would avoid use eval, so I take @PaulHodge's answer.
-1

Your logic looks good from inside the command file...
See https://www.gnu.org/software/bash/manual/bash.html#Simple-Command-Expansion

The problem is that you are loading the variable assignment into a variable, rendering it as data rather than a command, then trying to context-hack it back.

ENV_NUMBER=1 command

is not the same as

"ENV_NUMBER=1" "command"

which is what you are effectively doing.

Try this instead.

sed -Ei 's/$/\&/' comandlist.txt # make sure there are no blank lines first!

then either

chmod x comandlist.txt
./comandlist.txt

or

source comandlist.txt 

This makes comandlist.txt the script, complete with the ampersands, which it otherwise kind of already was. Your program reads it in as data and then tries to convert it back to script a line at a time. Don't do that.

c.f. https://mywiki.wooledge.org/BashFAQ/050

Original answer - for reference

Are you sure you are using bash?
Do you have something like #!/bin/bash as the first line of your script?

Here's my simple example script:

$: cat y
#! /bin/bash
echo "x=[$x]" # using whatever is available, not setting in the script

Running it with no x value:

$: unset x; ./y # nothing to show, x has no value
x=[]

Running it with an x set:

$ x=foo; ./y  # NOT exported to the subshell! Can't see it.
x=[]

Explicitly setting it temporarily in the command itself:

$: x=foo ./y # this creates x in this subshell, goes away when it ends
x=[foo]

Running again without setting to show it's temporary...

$ ./y  # didn't keep from last run
x=[]

Explicitly exporting in the parent environment:

$: export x=bar; ./y # reads the exported value from the parent
x=[bar]

Manually overriding the exported value by supplying a temporary value from the command line:

$: x=foo ./y # overrides exported value of "bar" in the subshell *only*
x=[foo]

Running again with no special edit, using the still-exported value:

$: ./y   # x still exported as "bar"
x=[bar]

So obviously what you are doing works fine in bash.
As I mentioned above, maybe you are not using bash.
Make sure you have a shebang. #! have to be the very first two characters in the file for it to work - no spaces above or before, no comments, nothing.

Let us know if that doesn't help.

2 Comments

Left the original text in case the downvote was for something untrue in there - hopefully someone will point it out. Corrected the answer for what I suspect is the real problem.
Thank you. I understand what the problem was. Making commandlist.txt the script works and is the best way to me this time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.