Persistently set the value of an environment variable for all users
Content |
Tested on |
Debian (Etch, Lenny, Squeeze) |
Ubuntu (Hardy, Intrepid, Jaunty, Karmic, Lucid, Maverick, Natty, Oneiric, Precise) |
Objective
To persistently set the value of an environment variable for all users
Background
Environment variables are name-value pairs that can be used to communicate information from a process to its descendants. They are typically used to provide programs with information about the environment in which they are executing (hence the name). Notable ones include:
DISPLAY |
the local or remote X Window display that should be used by default |
PATH |
the list of paths to search when looking for an executable |
PWD |
the current working directory |
TERM |
the terminal type |
TZ |
the default timezone |
Environment variables are inherited from parent to child when new processes are created. Processes can freely alter their own environment variables but not those of other processes. In particular, changes made by a child process do not propagate back to its parent.
Scenario
Suppose that you want users on the local machine to direct HTTP requests via a proxy server listening on port 8080 of proxy.example.com
. This can be achieved for many (but not all) HTTP client programs by setting the environment variables http_proxy
and HTTP_PROXY
to http://proxy.example.com:8080/
.
(The lower case variant http_proxy
is the more widely accepted form, and some HTTP clients accept either, but for maximum coverage it is better to set both.)
Method
On most Linux-based systems, the first instance of the shell created following a user login will look for a file called /etc/profile
, and if it exists and is readable then it will execute the content of that file as shell script. Importantly it does this without spawning a child process, making /etc/profile
a suitable location for setting environment variables. Here is a typical example, with the required variable assignments placed at the end:
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
if [ "`id -u`" -eq 0 ]; then
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
PATH="/usr/local/bin:/usr/bin:/bin:/usr/games"
fi
if [ "$PS1" ]; then
if [ "$BASH" ]; then
PS1='\u@\h:\w\$ '
else
if [ "`id -u`" -eq 0 ]; then
PS1='# '
else
PS1='$ '
fi
fi
fi
export PATH
umask 022
export http_proxy='http://proxy.example.com:8080/'
export HTTP_PROXY="$http_proxy"
Note that because of the manner in which this file is executed by the shell there is no need for it to begin with a shebang line (for example, #!/bin/sh
).
The value of HTTP_PROXY
has been defined in terms of http_proxy
so that changes can be made by editing one line rather than two. The use of single quotation marks in the first assignment causes interpolation to be suppressed, with the result that only single quotation marks within the URL need be escaped. The double quotation marks in the second assignment allow interpolation (which is wanted) but suppress tokenisation (which is not). In this particular example the quotation marks could be omitted because there are no special characters, but it is good practice to quote to protect against future changes.
Limitations
OpenSSH
The content of /etc/profile
is executed only when a login shell is created. One situation where this does not occur is when OpenSSH is used to execute a command non-interactively, for example:
ssh user@example.com 'env'
It is possible to work around this by explicitly creating a login shell as part of the remote command:
ssh user@example.com 'sh -l -c env'
sudo
Similar considerations apply when executing commands using sudo
, even if you start an interactive shell using the -s
option. To obtain a login shell you can use the -i
option instead.
cron
Command run from a crontab do not execute within a login shell. As with ssh
you can work around this by explicitly creating a login shell:
* * * * * sh -l -c /home/user/myscript.pl
LTSP
Some versions of LTSP (the Linux Terminal Server Project) create sessions without creating a login shell, therefore /etc/profile
is not executed. The suggested workaround is to use /etc/bash.bashrc
, which is not entirely satisfactory for the reasons discussed below.
Other methods of command execution
This is not an exhausive list of ways in which /etc/profile
can be bypassed, and it would be unwise to rely on the method described here without first testing it under the particular set of circumstances in which it will be used.
Testing
You can check that the environment variable has been exported with the intended value by inspecting it from within a new login shell. The login shell can be created using the -l
option of the sh
command. The value can be inspected using the printenv
command if it is available:
sh -l -c 'printenv http_proxy'
or the printf
command if not:
sh -l -c 'printf "%s\n" "$http_proxy"'
Alternatives
Using /etc/environment
On systems that use the pam_env
PAM module (which most general-purpose Linux-based systems do), the file /etc/environment
can be used to set environment variables during login. It should contain a list of name-value pairs, one per line with an equals sign between each name and value. It is not interpreted as a shell script. To set the http_proxy
and HTTP_PROXY
variables as described above, suitable content would be:
http_proxy=http://proxy.example.com:8080/ HTTP_PROXY=http://proxy.example.com:8080/
As with /etc/profile
there are some circumstances where this file is not used, but there are differences as to what those circumstances are. For example:
- commands run non-interactively using OpenSSH will see variables from
/etc/environment
but not from/etc/profile
, whereas - interactive OpenSSH sessions run through a server with the
UsePAM
option set tono
will typically see variables from/etc/profile
but not from/etc/environment
.
Support for /etc/environment
is not required by the LSB (as of version 4.1), but support is widespread and not restricted to Linux-based systems. Generally it is a reasonable alternative to /etc/profile
on systems that support it.
Using /etc/bash.bashrc
The file /etc/bash.bashrc
is executed at startup by most interactive instances of the Bash shell that are not login shells.
This makes it potentially useful for filling gaps in the coverage of /etc/profile
. Points to bear in mind:
- This configuration file is specific to Bash and will not work if the user has chosen to use a different shell.
- Rather than merely providing a default value for an environment variable at the start of a session,
/etc/bash.bashrc
has the potential to override assignments made by the user during a session.
Using separate assignment and export statements
Some non-POSIX compatible shells lack support for the combined assignment and export command used above and instead require that these two operations be performed separately, for example:
http_proxy="http://proxy.example.com:8080/" ; export http_proxy
Scripts that use the combined form are acceptably portable for most purposes, it being reasonable to assume that the default shell is POSIX-compatible, but if you want to support the widest possible range of execution environments then there is some benefit in using separate commands.
See also
- Set the value of an environment variable
- Persistently set the value of an environment variable for a given user
- Display the value of an environment variable
- Add a directory to the current path
Tags: environment | shell