197

I have written the following script to set some environment variables when needed:

#!/bin/sh
export BASE=/home/develop/trees
echo $BASE
export PATH=$PATH:$BASE
echo $PATH

Below the command and the results I can see on my terminal: the script runs, but the variables are not set at the end.

~$: ./script.sh
/home/develop/trees
/bin:......:/home/develop/trees
~$: echo $BASE

~$: 

What is wrong?

1
  • 1
    Shells are opened hierarchically. You can set a parent's shell's vars using . ./yourscript.sh Jan 29, 2020 at 14:54

3 Answers 3

281

export exports the variable assignment to child processes of the shell in which the export command was ran. Your command-line environment is the parent of the script's shell, so it does not see the variable assignment.

You can use the . (or source) bash command to execute the script commands in the current shell environment and achieve what you want, e.g.

source ./script.sh
echo "$BASE"

Will produce

/home/develop/trees

The source command, often seen in scripts, is a bash synonym for ., which is part of the POSIX standard (so . is available in dash, for example, but source isn't).

. ./script.sh     # identical to "source ./script.sh"

(. script.sh and source script.sh will first look for script.sh in PATH, so it's safer to specify the path to script.sh.)

3
  • 35
    You don't need export to pass variables on to subshells, a subshell is a copy of your current shell, including variables and functions etc. Exported variables gets copied to new processes spawned from the shell, regardless of that process being another shell or not. Secondly, . is the POSIX command for sourcing. Bash adds source as a more readable synonym for it, but you can't rely on it being available in sh. Lastly . ./script instead of . script if you want to avoid surprises. mywiki.wooledge.org/BashFAQ/060
    – geirha
    Jul 18, 2011 at 5:55
  • Note that if you source a script AND use pipe then the sourced environment is not available to parent. e.g. 'source setit.sh' is okay. 'source setit.sh |tee setit.log' is not okay. Surprising. Not intuitive.
    – gaoithe
    Sep 8, 2016 at 11:47
  • Just learned the hard way that if you are in the habit of using set -euo pipefail in all your scripts if you source that script you will set it in the current terminal which means the terminal will exit when it receives a non-zero exit code (ie a failure). Not the best experience. What you can do is call set +euo pipefail at the end of the script to turn it back off but still have the safety of failures exiting your script. Jul 30 at 1:48
22

When you run a script, it runs in a subshell. Variables are only valid within the context of that subshell. Set them in your .bashrc or .profile and read up on variables and subshells. The export statement works down hierachy (current shell and all it's subshells) not up as in your example.

Alternatively (if you really want the script to effect the enviroment of your current shell) run it as:

. ./script.sh

That causes it to run in your current shell but will not pass variables up the hierarchy either.

0
7

I often want to set an environment variable without hassle.

Here is what I add to my .bashrc to implement this convenience.

defect() {
    if [ $1 ] && [ -z $2 ]
    then
        eval 'export DEFECT=$1'
        return 0
    else
        echo 'Usage: defect {number}'
        return 1
    fi
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.