An environment is not as magical as it might seem. The shell stores it in memory and passes to the execve()
system call. The child process inherits it as an array pointer called environ
. From the execve
manpage:
SYNOPSIS
#include <unistd.h>
int execve(const char *filename, char *const argv[],
char *const envp[]);
argv
is an array of argument strings passed to the new program.
By convention, the first of these strings should contain the filename
associated with the file being executed. envp
is an array of strings,
conventionally of the form key=value, which are passed as environment
to the new program.
The environ(7)
manpage also offers some insight:
SYNOPSIS
extern char **environ;
DESCRIPTION
The variable environ
points to an array of pointers to strings
called the "environment". The last pointer in this array has the
value NULL
. (This variable must be declared in the user program,
but is declared in the header file <unistd.h>
in case the
header files came from libc4 or libc5, and in case they came from
glibc and _GNU_SOURCE was defined.) This array of strings is made
available to the process by the exec(3) call that started the process.
Both of these GNU manpages match the POSIX specification
If all the shells you're interested in are Bourne-compatible, you can use /etc/profile
for this purpose.
The header of /etc/profile
:
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...)
To ensure you've got csh
and tcsh
covered, you can also add your variables to /etc/csh.login
.
The header of /etc/csh.login
:
# /etc/csh.login: system-wide .login file for csh(1) and tcsh(1)
For zsh
, you want /etc/zshenv
.
For ease of maintenance
I would write all the variables you want in a single file and write a simple Perl (or other) script that would read these variables and update the relevant files for all the shells.
Something like the following in Perl should work:
#!/usr/bin/perl
use strict;
use warnings;
my $bourne_file = '/etc/profile';
my $csh_file = '/etc/csh.login';
my $zsh_file = '/etc/zshenv';
open my $BOURNE,'>>', $bourne_file;
open my $CSH,'>>', $csh_file;
open my $ZSH,'>>', $zsh_file;
while(<DATA>){
chomp;
my $delimiter = ','; #Change , to whatever delimiter you use in the file
my ($var, @value) = split /$delimiter/;
my $value = join $delimiter,@value;
print $BOURNE qq{export $var="$value"\n};
print $CSH qq{setenv $var "$value"\n};
print $ZSH qq{export $var="$value"\n};
}
close $BOURNE;
close $CSH;
close $ZSH;
__DATA__
var1,val1
var2,val2
You can use a delimiter other than ,
to delimit variables and values as long as this delimiter isn't allowed in variable names.
This can be further tidied up by inserting unique delimiters around the portion you want your script to write in each file so that each time you use the script to update the files, it doesn't duplicate previous entries but substitutes them in situ.
Best Answer
Traditionally, by login(1):
Though these days it might be a window manager or terminal program making those settings, depending on the flavor of unix and how far they've departed from tradition.
env
will show what's currently set in the environment, which a shell or something else may have altered from the default. However, "terminal settings" are not typically environment variables, and shells likebash
orzsh
have aset
command, and other places they hide settings...