Matias Barrios
Matias Barrios

Reputation: 5056

Establish environment variables dynamically in the command line

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

Answers (3)

rici
rici

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

Marcelo Castro
Marcelo Castro

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

Romeo Ninov
Romeo Ninov

Reputation: 7225

In the file printEnv.sh you can add as first line (after shebang) this line:

source environment.txt

Upvotes: 0

Related Questions