Modifying the environment from scripts

April 11, 2005 ·
About 3 minutes

Two days ago, a friend of mine was trying to modify the environment (the CLASSPATH) from within a shell script. Her intention was to run the script and then start working with Java on the same shell were the script was run. This didn't work. In order to explain why, I'm going to review the three different ways to run scripts and how they are related to the current environment.

Let's create a sample file, named foo.sh and stored in the home directory. Its purpose will be to modify a variable and create a function:

FOO_VAR=hello; export FOO_VARfoo_func() { echo "Hello, world!"}

Now, before running it, ensure that the environment is clean (i.e., that the variable is empty and that the function is undefined. To make things easier, just open a new terminal.

It's time to run the script. The first attempt will be to execute it using the shell interpreter explicitly:

$ sh ~/foo.sh$ echo ${FOO_VAR}$ foo_funcksh: foo_func: not found$

Oops! It didn't work. Why? I hear. Well. When you run the script this way, you are launching a shell sub-process in which the script is executed. Therefore, when the shell exits — which happens upon file's EOF — the modifications to the environment are lost.

Let's try it in a different way, this time using a shell bang. Edit the foo.sh script and add #!/bin/sh as the very first line (there can't be any spaces before this). Then, give it execute permissions and try again:

What? It didn't work either. This case is equivalent to the previous one; the only difference is that the OS creates the sub-shell for us: it sees the magic shell bang ("#!", or "#! /" in some old OSes) at the beginning of the file and uses the program name given there to run the script.

The last possibility — and only solution to the problem — is to run the script in the same shell process as we are working, so it will modify the current environment. Think of it as if you were manually typing the contents of the file in the window you are working on.

How is this done? Just call the program with a single dot before its name, separated by an space. This magic keyword tells the shell to "source" the given file:

$ . ~/foo.sh$ echo ${FOO_VAR}hello$ foo_funcHello, world!

It worked. Hope it's clear now! PS: The export keyword I used in the sample file is used to tell the shell to propagate the given shell variables to further sub-shells, not parent ones. If you don't use it, then the variable is private to the current instance of the interpreter and won't be visible from anywhere else.