451

Suppose that I have this script:

export.bash:

#! /usr/bin/env bash
export VAR="HELLO, VARIABLE"

When I execute the script and try to access to the $VAR, I don't get any value!

echo $VAR

Is there a way to access the $VAR by just executing export.bash without sourcing it?

vvvvv
  • 25,404
  • 19
  • 49
  • 81
Salah Eddine Taouririt
  • 24,925
  • 20
  • 60
  • 96
  • 1
    You could try using an alias instead of a script to define your variable. – jdigital May 17 '13 at 21:22
  • The script that I'm working on, is some kind of a java virtual manager , I do some computation then I should export a $JAVA_HOME to the env, and append it to PATH. – Salah Eddine Taouririt May 17 '13 at 21:24
  • Well, you could write the info to a file and then read it back in, but sourcing seems much easier. – jdigital May 17 '13 at 21:28
  • 25
    child processes cannot alter their parent's environment. The parent has to choose to alter itself (either with `source` or `eval` or ...) – glenn jackman May 17 '13 at 21:48
  • Thanks @glennjackman , I end up with by wrapping the commands in a function and source it from .bash_profile . – Salah Eddine Taouririt May 17 '13 at 22:53
  • possible duplicate of [Global environment variables in a shell script](http://stackoverflow.com/questions/1464253/global-environment-variables-in-a-shell-script) – tripleee Jan 05 '15 at 13:50
  • 3
    To read more on source, `man source` won't work because `source` is built into bash, you need to run `help source` – Peter Chaula Apr 30 '17 at 09:55
  • @peter - Wow. I can't believe I've been using `bash` for over a decade and this is the first time I've ever heard of `help`. – ArtOfWarfare Oct 16 '17 at 18:25
  • @ArtWarfare help also works as a reference for common shell constructs such as if, for...etc. – Peter Chaula Oct 16 '17 at 19:25

13 Answers13

484

Is there any way to access to the $VAR by just executing export.bash without sourcing it ?

Quick answer: No.

But there are several possible workarounds.

The most obvious one, which you've already mentioned, is to use source or . to execute the script in the context of the calling shell:

$ cat set-vars1.sh 
export FOO=BAR
$ . set-vars1.sh 
$ echo $FOO
BAR

Another way is to have the script, rather than setting an environment variable, print commands that will set the environment variable:

$ cat set-vars2.sh
#!/bin/bash
echo export FOO=BAR
$ eval "$(./set-vars2.sh)"
$ echo "$FOO"
BAR

A third approach is to have a script that sets your environment variable(s) internally and then invokes a specified command with that environment:

$ cat set-vars3.sh
#!/bin/bash
export FOO=BAR
exec "$@"
$ ./set-vars3.sh printenv | grep FOO
FOO=BAR

This last approach can be quite useful, though it's inconvenient for interactive use since it doesn't give you the settings in your current shell (with all the other settings and history you've built up).

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 1
    Could you please explain the third approach regarding `exec "$@"` and `printenv` thing? Also, how can 2nd and 3rd approach be used in the case where the script takes some arguments? – Darshan L Nov 16 '20 at 09:02
  • I've a script takes some arguments. `sudo . dd.sh $source $pathToUsb` command not found or `eval "$(sudo dd.sh $source $pathToUsb)"` sh: 1: eval: File: not found sh: 2: eval: File: not found or if trying `./dd.sh $source $pathToUsb printenv | grep line` can't exec "@" . Any ideas ? – kramer Feb 07 '21 at 10:07
  • The `.` command is built into the shell, so it's not visible to `sudo`. Your second command will try to `eval` the *output* of the `sudo dd.sh ...` command, and I have no idea what that output looks like. Does `dd.sh` print commands that can be evaluated by the shell? – Keith Thompson Feb 07 '21 at 21:06
  • Could you please clarify this : "doesn't give you the settings in your current shell "? – Erwann Apr 02 '21 at 03:19
  • 1
    @Erwann In the example I used, the command executed is `printenv`, which is affected only by environment variables. But if you invoke an interactive shell instead of `printenv`, then you have a new shell process, which doesn't inherit your current shell's history (except perhaps via `$HISTFILE`), non-exported variables, background jobs, etc. – Keith Thompson Apr 02 '21 at 03:54
  • @DarshanL - `"$@"` is an array-like construct of positional arguments (i.e. `$1`, `$2`, etc.) that you'd pass to a script. For 2nd approach, do something like `eval "$(./set-vars2.sh arg1 arg2)"`. For 3rd, you could account for `printenv` being your final argument, and do `for i; do :; done; exec "$i"` instead of `exec "$@"`. – solimant Apr 05 '22 at 19:07
86

In order to export out the VAR variable first, the most logical and seemly working way is to source the variable:

. ./export.bash

or

source ./export.bash

Now when echoing from the main shell, it works:

echo $VAR
HELLO, VARIABLE

We will now reset VAR:

export VAR=""
echo $VAR

Now we will execute a script to source the variable then unset it:

./test-export.sh
HELLO, VARIABLE
--
.

The code: file test-export.sh

#!/bin/bash
# Source env variable
source ./export.bash

# echo out the variable in test script
echo $VAR

# unset the variable
unset VAR
# echo a few dotted lines
echo "---"
# now return VAR which is blank
echo $VAR

Here is one way:

Please note: The exports are limited to the script that execute the exports in your main console - so as far as a cron job I would add it like the console like below... for the command part still questionable: here is how you would run in from your shell:

On your command prompt (so long as the export.bash file has multiple echo values)

IFS=$'\n'; for entries in $(./export.bash); do  export $entries;  done; ./v1.sh
HELLO THERE
HI THERE

File cat v1.sh

#!/bin/bash
echo $VAR
echo $VAR1

Now so long as this is for your usage - you could make the variables available for your scripts at any time by doing a Bash alias like this:

myvars ./v1.sh
HELLO THERE
HI THERE

echo $VAR

.

Add this to your .bashrc file:

function myvars() {
    IFS=$'\n';
    for entries in $(./export.bash); do  export $entries;  done;

    "$@";

    for entries in $(./export.bash); do variable=$(echo $entries|awk -F"=" '{print $1}'); unset $variable;
    done
}

Source your .bashrc file and you can do like the above any time...

Anyhow back to the rest of it...

This has made it available globally then executed the script...

Simply echo it out and run export on the echo!

File export.bash

#!/bin/bash
echo "VAR=HELLO THERE"

Now within script or your console run:

export "$(./export.bash)"

Try:

echo $VAR
HELLO THERE

Multiple values so long as you know what you are expecting in another script using the above method:

File export.bash

#!/bin/bash
echo "VAR=HELLO THERE"
echo "VAR1=HI THERE"

File test-export.sh

#!/bin/bash

IFS=$'\n'
for entries in $(./export.bash); do
    export $entries
done

echo "round 1"
echo $VAR
echo $VAR1

for entries in $(./export.bash); do
    variable=$(echo $entries|awk -F"=" '{print $1}');
    unset $variable
done

echo "round 2"
echo $VAR
echo $VAR1

Now the results

./test-export.sh
round 1
HELLO THERE
HI THERE
round 2


.

And the final final update to auto assign, read the VARIABLES:

./test-export.sh
Round 0 - Export out then find variable name -
Set current variable to the variable exported then echo its value
$VAR has value of HELLO THERE
$VAR1 has value of HI THERE
round 1 - we know what was exported and we will echo out known variables
HELLO THERE
HI THERE
Round 2 - We will just return the variable names and unset them
round 3 - Now we get nothing back

The script:

File test-export.sh

#!/bin/bash

IFS=$'\n'
echo "Round 0 - Export out then find variable name - "
echo "Set current variable to the variable exported then echo its value"
for entries in $(./export.bash); do
    variable=$(echo $entries|awk -F"=" '{print $1}');
    export $entries
    eval current_variable=\$$variable
    echo "\$$variable has value of $current_variable"
done


echo "round 1 - we know what was exported and we will echo out known variables"
echo $VAR
echo $VAR1

echo "Round 2 - We will just return the variable names and unset them "
for entries in $(./export.bash); do
    variable=$(echo $entries|awk -F"=" '{print $1}');
    unset $variable
done

echo "round 3 - Now we get nothing back"
echo $VAR
echo $VAR1
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
V H
  • 8,382
  • 2
  • 28
  • 48
  • 2
    I want to export VAR by executing the file, not by sourcing it. – Salah Eddine Taouririt May 17 '13 at 21:35
  • @vahid you should explain what your script is doing. – FDinoff May 17 '13 at 21:49
  • 1
    why not unset the variable after sourcing ? it be a lot easier than the pain your trying to cause yourself will update the test-export script – V H May 17 '13 at 21:50
  • I gave an alternative way of doing it Tarrsalah – V H May 17 '13 at 22:11
  • ok final updates - issues with the suggested method and work arounds the bashrc alias idea would probably be a great way forth – V H May 17 '13 at 23:21
  • I am not sure why the author says he does not want to source the script. That just seems like this is exactly the command to achieve what he wants. – seba.wagner Sep 07 '16 at 01:22
  • You seem to have the knowledge that I need, but am still trying to grasp. If you could take a look at this question here and see if you know an answer I'd be very appreciative: https://askubuntu.com/questions/1209780/open-terminal-with-multiple-tabs-and-execute-application-which-uniquely-modifies. – Gabriel Staples Feb 11 '20 at 20:55
47

Execute

set -o allexport

Any variables you source from a file after this will be exported in your shell.

source conf-file

When you're done execute. This will disable allexport mode.

set +o allexport
mdornfe1
  • 1,982
  • 1
  • 24
  • 42
  • 20
    The entire point of the question is "How to export without source". This answer uses source. – Gonen I Jan 13 '19 at 10:07
  • 4
    @ekkis Yeah it doesn't exactly answer the question, but whenever I googled how to export variables from a file this thread came up. So I kind of just put it there for myself. – mdornfe1 Feb 24 '19 at 02:38
  • 4
    I upvoted this because the original problem statement lead me to understand that this was my fundamental solution. There's official docs on allexport here: https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html – benjaoming Nov 15 '19 at 18:45
22

I found an interesting and neat way to export environment variables from a file:

In file env.vars:

foo=test

Test script:

eval `cat env.vars`
echo $foo         # => test
sh -c 'echo $foo' # =>

export eval `cat env.vars`
echo $foo         # => test
sh -c 'echo $foo' # => test

# a better one. "--" stops processing options,
# key=value list given as parameters
export -- `cat env.vars`
echo $foo         # => test
sh -c 'echo $foo' # => test
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Xiaofan Hu
  • 1,482
  • 1
  • 16
  • 22
13

Another workaround that, depends on the case, it could be useful: creating another bash script that inherits the exported variable. It is a particular case of Keith Thompson's answer, will all of those drawbacks.

File export.bash:

# !/bin/bash
export VAR="HELLO, VARIABLE"
bash

Now:

./export.bash
echo $VAR
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gonmator
  • 760
  • 6
  • 15
  • Are you sure that what you wrote works!? I believe that you have to execute the line command: `. ./export.bash` – Sir Jo Black Nov 08 '18 at 09:43
  • @Sir Jo Black, yes, sure. The last line of the export.bash file invoke `bash` as child of the current shell. And since `VAR` was exported before, that invocation will include the variable. Of course, if you type `exit` the `VAR` will go out. Have you checked? – Gonmator Nov 09 '18 at 14:06
  • Ok! I've not seen that invocation ... :p – Sir Jo Black Nov 11 '18 at 09:53
  • This will create a new bash shell, the old one will keep running. If you wanted to exit, you must exit twice, from the new one and then from the old one, that still do not have the exported environment variables. – fflores Oct 25 '22 at 12:55
  • @fflores As it says, depends on the case, it could be useful. In other cases is not, for instance, because creating the second interactive bash. – Gonmator Oct 27 '22 at 09:02
4

The answer is no, but for me I did the following

The script:

myExport

#! \bin\bash
export $1

An alias in my .bashrc file:

alias myExport='source myExport'

Still you source it, but maybe in this way it is more useable and it is interesting for someone else.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Malcomar
  • 94
  • 5
4

Export environment variables using script file

Problem:
When you run a script, it executes in a child shell and returns back to the parent shell after execution. Exporting variables only works down the child shells, you can't export a child shell's variable back to the parent shell.

Solution:
From your script file invoke a child shell along with variables that you want to export, this will create a new child shell with your variables exported.

script.sh ->

bash -c 'export VAR=variable; exec bash'

: : CIPH3R

Export Variables using script

ARAVIND SWAMI
  • 747
  • 7
  • 10
2

Maybe you can add a function in ~/.zshrc or ~/.bashrc.

# set my env
[ -s ~/.env ] && export MYENV=`cat ~/.env`
function myenv() { [[ -s ~/.env ]] && echo $argv > ~/.env && export MYENV=$argv }

Because of the use of a variable outside, you can avoid the use of a script file.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
zhi.yang
  • 425
  • 5
  • 10
1

simple naive approach that works:

script 1:

echo "something" > /tmp/myvar

script 2:

myvar=$(cat /tmp/myvar)

br4nnigan
  • 646
  • 6
  • 13
1

If you want to set a variable for the calling shell there is a robust approach using c unix sockets. The result will be tested like this:

$ ./a.out &
[1] 5363
$ ./b.out 123;a=`./b.out`;echo ${a}
123
[1]+  Done                    ./a.out

See a.c, b.c, and a.h from my github.

Sam
  • 91
  • 1
  • 5
0

I don't think this can be done, but I found a workaround using alias. It will only work when you place your script in your scripts directory. Otherwise your alias will have an invalid name.

The only point to the workaround is to be able to have a function inside a file with the same name and not have to bother sourcing it before using it. Add the following code to file ~/.bashrc:

alias myFunction='unalias myFunction && . myFunction && myFunction "$@"'

You can now call myFunction without sourcing it first.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dan Bray
  • 7,242
  • 3
  • 52
  • 70
0

This workaround is somehow hinted to elsewhere, but maybe not that clearly:

In your script, after setting the variable, start a new shell, rather than return.

My use cases is that I have a number of terminals open and in some of them I want some values for some variables, while in others I want other values.

As using source may be harder to remember, a small advantage of this approach is when it takes a while to realize that you forgot to use source, and you have to start from scratch.

(For me it makes more sense to use source script, as the missing variables are noticed immediately.)

ciobi
  • 97
  • 1
  • 10
0

I had similar problem calling ssh-agent -s in a script called by option -e in rsync.

In the script eval $(ssh-agent -s) don't preserve the environment variables for the next call.

rsync -e 'source ssh-check-agent.sh -p 8022' does not work, so I made a workaround. In the script I saved the variables in a temporal file after call ssh-agent like:

echo "export SSH_AUTH_SOCK=$SSH_AUTH_SOCK;" > /tmp/ssh-check-agent.vars
echo "export SSH_AGENT_PID=$SSH_AGENT_PID;" >> /tmp/ssh-check-agent.vars

and after in the script that calls rsync (backup.sh) I call:

source /tmp/ssh-check-agent.vars

The problem is that script that calls rsync must be called by source (source backup.sh).

I know that is not the question (I use two times source), but I put here if someone has similar problem with rsync.

oml
  • 115
  • 2
  • 8