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
Related
How to set a global environment variable in a bash script?
If I do stuff like
#!/bin/bash
FOO=bar
...or
#!/bin/bash
export FOO=bar
...the vars seem to stay in the local context, whereas I'd like to keep using them after the script has finished executing.
Run your script with .
. myscript.sh
This will run the script in the current shell environment.
export governs which variables will be available to new processes, so if you say
FOO=1
export BAR=2
./runScript.sh
then $BAR will be available in the environment of runScript.sh, but $FOO will not.
When you run a shell script, it's done in a sub-shell so it cannot affect the parent shell's environment. You want to source the script by doing:
. ./setfoo.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.
The file searched for in PATH need not be executable. When bash is not
in POSIX mode, the current directory is searched if no file is found
in PATH.
If the sourcepath option to the shopt builtin command is turned off,
the PATH is not searched.
If any arguments are supplied, they become the positional parameters
when filename is executed.
Otherwise the positional parameters are unchanged. The return status
is the status of the last command exited within the script (0 if no
commands are executed), and false if filename is not found or cannot
be read.
source myscript.sh is also feasible.
Description for linux command source:
source is a Unix command that evaluates the file following the command,
as a list of commands, executed in the current context
#!/bin/bash
export FOO=bar
or
#!/bin/bash
FOO=bar
export FOO
man export:
The shell shall give the export attribute to the variables corresponding to the specified names, which shall cause them to be in the environment of subsequently executed commands. If the name of a variable is followed by = word, then the value of that variable shall be set to word.
A common design is to have your script output a result, and require the cooperation of the caller. Then you can say, for example,
eval "$(yourscript)"
or perhaps less dangerously
cd "$(yourscript)"
This extends to tools in other languages besides shell script.
In your shell script, write the variables to another file like below and source these files in your ~/.bashrc or ~/.zshrc
echo "export FOO=bar" >> environment.sh
In your ~/.bashrc or ~/.zshrc, source it like below:
source Path-to-file/environment.sh
You can then access it globally.
FOO=bar
export FOO
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.
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
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
How to set a global environment variable in a bash script?
If I do stuff like
#!/bin/bash
FOO=bar
...or
#!/bin/bash
export FOO=bar
...the vars seem to stay in the local context, whereas I'd like to keep using them after the script has finished executing.
Run your script with .
. myscript.sh
This will run the script in the current shell environment.
export governs which variables will be available to new processes, so if you say
FOO=1
export BAR=2
./runScript.sh
then $BAR will be available in the environment of runScript.sh, but $FOO will not.
When you run a shell script, it's done in a sub-shell so it cannot affect the parent shell's environment. You want to source the script by doing:
. ./setfoo.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.
The file searched for in PATH need not be executable. When bash is not
in POSIX mode, the current directory is searched if no file is found
in PATH.
If the sourcepath option to the shopt builtin command is turned off,
the PATH is not searched.
If any arguments are supplied, they become the positional parameters
when filename is executed.
Otherwise the positional parameters are unchanged. The return status
is the status of the last command exited within the script (0 if no
commands are executed), and false if filename is not found or cannot
be read.
source myscript.sh is also feasible.
Description for linux command source:
source is a Unix command that evaluates the file following the command,
as a list of commands, executed in the current context
#!/bin/bash
export FOO=bar
or
#!/bin/bash
FOO=bar
export FOO
man export:
The shell shall give the export attribute to the variables corresponding to the specified names, which shall cause them to be in the environment of subsequently executed commands. If the name of a variable is followed by = word, then the value of that variable shall be set to word.
A common design is to have your script output a result, and require the cooperation of the caller. Then you can say, for example,
eval "$(yourscript)"
or perhaps less dangerously
cd "$(yourscript)"
This extends to tools in other languages besides shell script.
In your shell script, write the variables to another file like below and source these files in your ~/.bashrc or ~/.zshrc
echo "export FOO=bar" >> environment.sh
In your ~/.bashrc or ~/.zshrc, source it like below:
source Path-to-file/environment.sh
You can then access it globally.
FOO=bar
export FOO