When you execute a shell script it inherits all the environment variables present in the parent shell. Sometimes that can cause unintended consequences. For example, I recently ran into a situation where one of my scripts in common use by our group failed for one of the users. I eventually tracked it down to the JAVA_HOME variable that user had set in his bash profile. The user’s JAVA_HOME being inherited by the script was not set to an appropriate value. Ideally the script should have set all the environment variables it needs. However, in this case, I had failed to explicitly set JAVA_HOME in my script, an oversight masked by the fact my own JAVA_HOME was set to a valid value so the script ran normally for me.
To reduce the chance of this type of problem reoccurring with other variables I decided to clear all the inherited environment variables at the start of the script. That way any other environment dependency not defined by the script would immediately reveal itself during the script debugging phase.
I could not find a simple builtin method to run the script without the inherited environment, (aside from the impractical “env -i scriptname“) [ edit 8/28/2010: see Gordon's env - /bin/bash suggestion below and you probably don't need to read the rest of this posting ], so I put a one-liner in the script to unset each variable (BASH syntax):
unset $(/usr/bin/env | egrep '^(\w+)=(.*)$' | \ egrep -vw 'PWD|USER|LANG' | /usr/bin/cut -d= -f1);
env prints the environment’s variable=value pairs. The cut at the end splits the pairs and returns the variable name.
The first egrep filters for the variable=value pairs that start at the beginning of the line. This was necessary because GNU screen sets a multiline TERMCAP variable:
TERMCAP=SC|screen|VT 100/ANSI X3.64 virtual terminal:\
:DO=\E[%dB:LE=\E[%dD:RI=\E[%dC:UP=\E[%dA:bs:bt=\E[Z:\
:cd=\E[J:ce=\E[K:cl=\E[H\E[J:cm=\E[%i%d;%dH:ct=\E[3g:\
I wanted just the first line and needed to discard the others so the cut would not split them and return bogus variable names, like ‘:DO‘, to unset.
The second egrep is optional but allows me to skip selected variables so they retain their preset values.
With this in place, if I attempt to use an environment variable in my script (or in a program called by my script) it will fail unless I explicitly set the value. This provides me the opportunity to ensure the script will behave consistently for all users on the system.

16 comments
Comments feed for this article
November 26, 2008 at 1:44 pm
Holger
at this point: thank you.
this really helps debugging hooks on svn and other scripts that are (mostly for security reasons) executed with an empty environment.
March 12, 2010 at 4:13 am
Noah Spurrier
The following does the same thing, but it a tiny bit shorter and more clear:
unset $(env | grep -o ‘^[_[:alpha:]][_[:alnum:]]*’ | \
grep -v -E ‘^PWD$|^USER$|^LANG$’)
Note the ‘-o’ option in the first grep. This shows you only the
parts that match on the string, so this gets rid of the need for the `cut`
part of your command. I also added anchors to the filter on PWD, USER, and LANG. There are lots of variables that have USER and LANG in their name. Considering your application I think you want to be explicit about matching these.
March 12, 2010 at 4:35 am
Noah Spurrier
Also, you might want to keep TERM, LC_.* and SSH_.* variables:
March 12, 2010 at 5:18 am
crashingdaily
Thanks for the suggestions.
The -w option for egrep matches whole words, so generally takes care of the anchoring. I suppose there might be some valid variable name that doesn’t begin/end with a word character that will break the whole word matching.
(e.g. -USER is not valid in my bash; maybe something similar is). Still, your explicit anchoring is safer and allows for stemming in cases like ^LC_* .
In my specific use case I didn’t need anything except PWD, USER, LANG but you raise good points for others to consider.
August 18, 2010 at 7:13 pm
Gordon McGregor
I know, I know, a really old post, but still
#!/bin/env – /bin/bash
at the top of your script would have the same effect
August 25, 2010 at 12:09 am
crashingdaily
It’s not too old to offer a good tip. I was too quick to dismiss “env -i scriptname”; I didn’t think it through enough to realize I could use bash in place of the script. Thanks, Gordon!
August 28, 2010 at 8:33 am
حذف کابر lfs « IRanuX
[...] Clearing environment variables in BASH scripts برچسب زده شده با:environment variables [...]
October 12, 2010 at 2:35 pm
Alex
My variant is
unset -v `env | sed -e ‘s/=.*//’`
- this was a question in the UNIX quiz held yesterday by yandex.ru
March 9, 2011 at 2:56 pm
tim
Using Bash we might also use the compgen shell builtin to get the names of inherited functions and variables.
compgen -A function
compgen -A variable
(though the env solution mentioned above seems best for emptying the shell environment)
November 16, 2011 at 10:27 am
The ‘backup’ gem needs a HOME « chop-mode
[...] Back on the server, I clear all existing environment vars with a snippet I found here: [...]
November 29, 2011 at 10:40 pm
The ‘backup’ gem needs a HOME | Full Of Stars
[...] Back on the server, I clear all existing environment vars with a snippet I found here: [...]
September 14, 2012 at 7:42 pm
Lee Culver
Thank you for posting this. it helped me a lot!
November 23, 2012 at 5:19 am
LinuxNewbie
When I tried using the env variable (JAVA_HOME) inside my script, it wasn’t accessible. It returned an empty value coz the bash script was running in a sub shell. The parent env variables weren’t inherited into the script. To use them, I had to run with ” source bashscriptname “. How did your script inherit the parent shell env variables?
November 24, 2012 at 7:36 pm
crashingdaily
You need to “export” the variable.
export JAVA_HOME=/java
See http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html#sect_03_02_03
For maximum portability across various Bourne shell flavors, you’ll want to break it into two statements
JAVA_HOME=/java
export JAVA_HOME
http://serverfault.com/questions/103999/how-should-i-export-a-shell-variable-in-bash
November 30, 2012 at 9:09 am
mmarusska
I don’t see “#!/bin/env – /bin/bash” working, on Linux. the whole rest of the line “- /bin/bash” is passed as _one_ argument to env(1), and that does not make sense to it — the space as option is invalid.
November 30, 2012 at 10:02 am
mmaruska
And yes, I use a different approach: I insist on invoking env(1), and passing “over” all the variables I want to preserve ….. to “itself”.
I.e. script A at the beginning invokes: exec env -i var1=value var2=value2 … “$0″ “$@”
So, to keep things in 1 file (rather than A invoking B-through-env), I need a “switch” to distinguish between the first call and the sanitized invocation (by env(1).