Exporting Unix Shell Environment Variables

Unexported Environment Variables | Changing the Effective User

The Unix shell offers the ability to set, modify, or unset environment variables, and to mark whether or not environment variables are exported to subprocesses. Complications arise depending on whether the subprocess is another shell or some other executable, and if a shell, whether that shell process is a login shell or interactive. Other concerns arise should the executable change the effective user—sudo(8), for example.

The following examples assume a Bourne shell.

Unexported Environment Variables

Environment variables that have not been exported are unavailable to subprocesses. This can cause unexpected behavior, especially if someone forgets to export the environment variable, or if the shell does not read the “run command” (rc) file where the export is made. Debugging requires determining whether or not the variable is exported, and whether the problem is due to an environment variable setting, or something else. These issues are usually complicated by complex shell configuration spread across any number of different global and user shell configuration files.

Consider a custom location for Unix man(1) pages. These custom locations are usually handled by setting appropriate values in the MANPATH environment variable:

$ cd /var/tmp
$ ls share/man/man1/
bar.1 foo.1
$ man foo
No manual entry for foo
$ man bar
No manual entry for bar

A naive setting of MANPATH will lack an export, though this will make the shell show that MANPATH has been set:

$ echo $MANPATH

$ cat manrc
MANPATH=/var/tmp/share/man
$ . /var/tmp/manrc
$ echo $MANPATH
/var/tmp/share/man
$ man foo
No manual entry for foo

MANPATH has not been marked for export, so is not available to subprocesses, such as perl:

$ echo $MANPATH
/var/tmp/share/man
$ perl -le 'print $ENV{MANPATH}'

$  

Once a variable is marked for export (this need only be done once per shell process; exporting a environment variable multiple times is like voting for the same candidate twice on a single ballot), subprocesses will obtain the setting:

$ export MANPATH
$ perl -E 'say $ENV{MANPATH}'
/var/tmp/share/man
$ man foo

Programs usually offer multiple ways to set configuration values; man(1) offers a -M argument, which performs the same role as the MANPATH environment variable, while ruby offers both RUBYLIB and a -I argument to set a custom library path. These can be used to test whether the problem is with the environment variable, or something else:

$ man bar
No manual entry for bar
$ ls share/man/man1/bar.1
share/man/man1/bar.1
$ echo $MANPATH
/var/tmp/share/man
$ perl -E 'say $ENV{MANPATH}'
/var/tmp/share/man
$ man -M /var/tmp/share/man bar
No manual entry for bar

The bar.1 man page does exist, and the problem is not due to MANPATH, as the equivalent -M option also fails. The actual problem here is a permissions problem, and a highly misleading message from the instance of man involved:

$ ls -l share/man/man1/*1
---------- 1 jmates jmates 6747 Jun 28 21:38 share/man/man1/bar.1
-r--r--r-- 1 jmates jmates 6747 Jun 28 21:38 share/man/man1/foo.1
$ chmod +r share/man/man1/*1
$ ls -l share/man/man1/*1
-r--r--r-- 1 jmates jmates 6747 Jun 28 21:38 share/man/man1/bar.1
-r--r--r-- 1 jmates jmates 6747 Jun 28 21:38 share/man/man1/foo.1
$ man bar

Other instances of man, for example on OpenBSD, instead print an informative message:

$ man bar
man: Formatting manual page...
troff: fatal error: can’t open ‘…’: Permission denied

Vague and misleading error messages are unfortunately common in computing (disclaimer: I am the author of Acme::EdError). Develop methods to narrow down where a suspected problem is, and fully learn all the configuration options of the shell or software involved.

ZSH offers inspection of environment variables, which can help avoid needling to know perl or writing some other program outside the shell to show environment variables:

$ echo ${(t)MANPATH}
scalar-export-unique-special
$ echo ${(t)PAGER}
scalar-export

Shells also may offer options to disable parsing of the shell configuration (zsh -f), though the environment of the parent process may need to be sanitized somehow. Also consider testing under a new user account that has no custom shell configuration (though this will pickup any global run command definitions), or on a clean virtual instance. Note that some operating systems or programs offer other means of setting environment variables, for example ~/.MacOSX/environment.plist on Mac OS X.

Changing the Effective User

Concerns:

Digression: effective group changes also have subtleties. System V permissions (Solaris, Linux) use the effective group of the process when creating new directories. Therefore, changing the effective group of a process may cause directories to be created with unexpected permissions. On Linux, the *BSD semantics of inheriting the permissions from the parent directory can be enabled via a special filesystem mount option, or by setting the group sticky bit. Hence modefix -d g+s somedir via modefix, or using configuration management to ensure that directory trees have appropriate permissions.