Load environment variables from file just for the next script - bash

I have one of those .env files with several variables set, like:
VAR1=111
VAR2=222
I want to run a script (without changing it) that makes use of those variables.
I know I can source that file in the shell and export the variables from the file, like:
set -a
source myenvs.env # this is my .env file
set +a
./myscript.sh # this will use the variables
The problem with that is it loads all those variables in my shell, which I don't want (shell pollution.)
What I'm looking for is the equivalent of using env VAR1=111 VAR2=222 ./myscript.sh but getting those variables from the .env file instead.

Do that in a subshell and main shell's environment won't be altered. E.g:
(
set -a
source myenvs.env # this is my .env file
exec ./myscript.sh # avoid unnecessary fork()
)
The truth is I need to keep that script flexible to run with different combinations of settings. Imagine I have several .env files like development.env, stagin.env, repro-bug-123.env, etc.
A function would be more useful in that case.
with_env() (
set -a
source "$1"
shift
exec "$#"
)
with_env myenvs.env ./myscript.sh

Declare vars that you don't want to change as read only
declare -r VAR3 VAR4
and source the rest
. ./myenvs.env

Related

set options in scope of a `source`ed script

I'm writing a shell script that sets enviornment variables. It will be sourced in other scripts like
source /path/to/my-script
I want my script to use set -euo pipefail but I don't want to affect the calling script.
This will affect the calling script.
# my-script
set -euo pipefail # affects the caller
export VAR=$(get-value)
If I wrap my script in a subshell the export won't work
# my-script
(
set -euo pipefail
export VAR=$(get-value) # not exported to the caller
)
AFAIK, if a file is sourced it is done to include all its parameters, so if you want to source a file partially, a workaround may be to exclude those parameters that you don't need and create a new temporary file with the rest of the parameters and eventually source the new file and delete it.
For your scenario, this workaround will do the job, use the following lines in your caller-script.sh to create my-script.tmp file from my-script file with lines starting with set excluded, source the my-script.tmp file and then remove it.
#!/bin/bash
grep -wv '^set' my-script > /tmp/my-script.tmp
source /tmp/my-script.tmp
rm /tmp/my-script.tmp

Define a variable in the .environment file and use (refrence) that in the .sh file

I have written a bash script to create a folder if it doesn't already exist and have set the variables in my environment file and referenced that in my .sh file
Below is my script I have added the variables in the .sh file which I have added in my environment file.
#!/bin/bash
if [ ! -d "$checkfolder" ]; then
mkdir -p "$createfolder";
fi
I have set the following environment variables in my /etc/environment file
checkfolder=/home/ubuntu/hello23
createfolder=/home/ubuntu/hello23
when I execute the script I am getting the following errors:
ubuntu#:~/cicd$ ./createfolder.sh
mkdir: cannot create directory ‘’: No such file or directory
Here is the normal script I have written, which works and creates a folder if it does not exist
if [ ! -d /home/ubuntu/hello ]; then
mkdir -p /home/ubuntu/hello;
fi
What is the issue here and what changes should i make?
/etc/environment and ~/.pam_environment are read when you log in. If you've just edited those files, your changes don't apply to the current session.
If you want to apply the changes to your current session, you can run
. /etc/environment
to set the variables in your current shell. This only works if the values of the variables don't contain any shell speciavairablel characters (spaces, ()[]{}\|&;<>~*?'"`$# and I may be forgetting a few), because the syntax of /etc/environment is only an approximation of the syntax of the shell.
If you've added new variables, and not just changed their value, you'll also need to export them (VAR=value in the shell only creates a shell variable, not an environment variable).
export MY_HOME checkfolder createfolder
or more generally
export $(sed -n 's/^\([A-Za-z0-9_]*\)=.*/\1/p')
There is no file that sets the environment for every new program, because that would defeat the point of the environment, which is that every process has its own which it inherits from its parents. If there was a “global environment file”, it would make it impossible to run programs with a different environment. If you want a global configuration, read a configuration file:
if [ -e ~/.my_application_configuration.sh ]; then
. ~/.my_application_configuration.sh
fi
There is a way to have a file that sets the environment for every bash script (only bash scripts, not sh scripts): put the full path to the file in the environment variable BASH_ENV. As I explained just above, this is usually a bad idea.

How can I export all bash variables declared in a file as environment variables?

Let us say I have a txt file vars.txt like this.
VAR1=32
VAR2=47
VAR3=78
Now if I simply do source vars.txt, these variables won't be exported as environment variables because I didn't prefix them with export.
So, my question is, is there a way to export all those variables without having to prefix them with export in the file?
Yes, there is a way: set -a
From help set
Options:
-a Mark variables which are modified or created for export.
To export every variable in your file you would do
set -a
source yourFile
set +a

Setting variables with spaces within .env

I have a .env file with a bunch of variables and I just came across an error.
One of the variables has spaces.
TEST="hello world"
So when I run this, learned about in this answer here.
env $(<.env)
I get this error.
env: world"': No such file or directory
How do I set variables with spaces within .env?
If your command is just a shell command, you could run your command in a subshell like this:
( . .env ; echo "$TEST" )
The source or . builtin has no problem with assignments containing spaces. It will set the variables in the .env file in the current shell's environment.
In the more likely case of calling an external program, you'll also have to add 'export' to each assignment in your env file like this:
export TEST="hello world"
This is necessary because source does not export assigned variables as env does, i.e. they are set inside the subshell only but not in the environment of another process started inside that subshell.
juste put the word that contains the space between " ".

Defining common variables across multiple scripts?

I have a number of Bash and Perl scripts which are unrelated in functionality, but are related in that they work within the same project.
The fact that they work in the same project means that I commonly specify the same directories, the same project specific commands, the same keywords at the top of every script.
Currently, this has not bitten me, but I understand that it would be easier to have all of these values in one place, then if something changes I can change a value once and have the various scripts pick up on those changes.
The question is - how is best to declare these values? A single Perl script that is 'required' in each script would require less changes to the Perl scripts, though doesn't provide a solution to the Bash script. A configuration file using a "key=value" format would perhaps be more universal, but requires each script to parse the configuration and has the potential to introduce issues. Is there a better alternative? Using environmental variables? Or a Bash specific way that Perl can easily execute and interpret?
When you run a shell script, it's done in a sub-shell so it cannot affect the parent shell's environment. So when you declare a variable as key=value its scope is limited to the sub-shell context. You want to source the script by doing:
. ./myscript.sh
This executes it in the context of the current shell, not as a sub shell.
From the bash man page:
. filename [arguments]
source filename [arguments]
Read and execute commands from filename in the current shell environment and return the exit status of the last command executed from filename.
If filename does not contain a slash, file names in PATH are used to find the directory containing filename.
Also you can use the export command to create a global environment variable. export governs which variables will be available to new processes, so if you say
FOO=1
export BAR=2
./myscript2.sh
then $BAR will be available in the environment of myscript2.sh, but $FOO will not.
Define environments variables :
user level : in your ~/.profile or ~/.bash_profile or ~/.bash_login or ~/.bashrc
system level : in /etc/profile or /etc/bash.bashrc or /etc/environment
For example add tow lines foreach variable :
FOO=myvalue
export FOO
To read this variable in bash script :
#! /bin/bash
echo $FOO
in perl script :
#! /bin/perl
print $ENV{'FOO'};
You could also source another file, so you do not create extra env variables, that may lead to unexpected behaviours.
source_of_truth.sh:
FOO="bar"
scritp1.sh
#!/usr/bin/env bash
source source_of_truth.sh
echo ${FOO}
# ... doing something
scritp2.sh
#!/usr/bin/env bash
source source_of_truth.sh
echo ${FOO}
# ... doing something else

Resources