Reputation: 5056
I have the following script :
#!/bin/bash
echo $FAV_FRUIT
echo $FAV_HERO
And the following txt file :
FAV_FRUIT='watermelon'
FAV_HERO='batman'
Im trying to understand why would this not work then :
$(cat environment.txt | tr '\n' ' ' ) ./printEnv.sh
bash: FAV_FRUIT='watermelon' FAV_HERO='batman' : command not found
But If I literally do this :
FAV_FRUIT='watermelon' FAV_HERO='batman' ./printEnv.sh
Then I get the right ouput.
How can I fix my command so it succeeds?
Upvotes: 2
Views: 2366
Reputation: 241701
I don't think that error message comes from the line you claim to have executed:
$ $(cat environment.txt | tr '\n' ' ' ) ./printEnv.sh
FAV_FRUIT='watermelon': command not found
Here's what I think you wrote. See the difference?
$ "$(cat environment.txt | tr '\n' ' ' )" ./printEnv.sh
FAV_FRUIT='watermelon' FAV_HERO='batman' : command not found
Anyway, the reason the $(...)
substitution is treated as a command (or, in the first case, a command and an argument) and not as temporary variable assignments is simple. The syntax var=value prog args
is just that: syntax. Bash parses the command line first, before doing any replacements, and it is during the parse that it identifies words which are temporary environment assignments.
After it parses the command, it figures out which words are temporary assignments and which ones are redirections, and separates them. It then does the various substitutions, after which it resplits the remaining words (except the ones which were quoted) and uses the first one as the command to execute. (The parameter assignments and redirections also undergo substitutions, but not word-splitting. And they are still assignments and redirections afterwards.)
That means that an assignment must look like an assignment when you type it. The variable name must be a valid variable name, so it cannot include any substitution. Only the value part undergoes substitutions.
The simplest way to accomplish what you seem to be trying to do is with the standard command-line utility env
. You could, for example, write:
env $(cat environment.txt | tr '\n' ' ' ) ./printEnv.sh
But watch out! The single quotes in your text file are not going to be removed when bash does the substitution. So the result will be:
'watermelon'
'batman'
rather than
watermelon
batman
To get the correct output, just leave the single quotes out of the file. Note that the tr
command is unnecessary, because word-splitting works on newline characters as well.
So far so good. But that will only work if no line in the file environment.txt
includes a space or tab character, or any other characters which might get expanded, such as *
. That's not very satisfactory. And you can't just quote the $(...)
, because then it will be a single argument which env
will treat as a single environment assignment (just as it was treated as a single word representing the command name in the original attempt).
The usual solution to this problem is to use a bash array. (Again, I removed the single quotes from environment.txt
so that this would work correctly.)
$ mapfile assignments < environment.txt
$ env "${assignments[@]}" ./printEnv.sh
watermelon
batman
Upvotes: 5
Reputation: 21
If you want the result of the $() to not be interpreted as a command, you can use env
:
env $(cat environment.txt | tr '\n' ' ' ) ./printEnv.sh
Unlike export
, env
only sets the variable temporarily. If you do echo $FAV_FRUIT
afterwards, you will get an empty output.
See https://ss64.com/bash/env.html
Upvotes: 2
Reputation: 7225
In the file printEnv.sh
you can add as first line (after shebang) this line:
source environment.txt
Upvotes: 0