env
and printenv
are printing the list of environment strings (meant to contain environment variable definitions) that is given to them by the command that executes them. The caller will eventually do a:
execve("/usr/bin/env", argv, envp);
system call where argv
and envp
are two lists of strings.
env
/printenv
just print the list of strings in envp
, one per line.
By convention, the strings in envp
are in the format var=value
, but they don't have to be (I don't know of any execve()
implementation that enforces it) and most env
, printenv
implementations don't care when they display them.
When the caller is a POSIX shell, it will include in the envp
that it passes to env
the list of its shell variables that are marked for export (either because the user called export
/typeset -x
on it, or because the variable was already in the environment that the shell received on start-up).
If some of the environment variables that the shell received on start-up could not be mapped to a shell variable, or if any of the envp
strings it received didn't contain a =
character, depending on the shell implementation, those strings will be passed along untouched, or the shell will strip them or some of them.
Example with bash
, using GNU env
to pass a list of arbitrary variable names (env
can't pass arbitrary envp strings though, they have to contain a =
, and the ones that use setenv()
can't pass some that start with =
¹).
$ env -i '=foo' '1=x' '+=y' bash -c printenv
+=y
1=x
[...]
(the variable with the empty name was removed but not the other ones).
Also, if the shell received multiple envp
strings for the same variable name, depending on the shell, they will all be passed along, or only the first one, or only the last one.
set
in POSIX shells prints the list of shell variables, including non-scalar ones for shells that support array/hash types, whether they've been marked for export or not.
In POSIX shells you can also use export -p
to list the variables that have been marked for export. Contrary to env
/printenv
, that also lists variables that have been marked for export but not be given any value yet.
In Korn-like shells like ksh
, zsh
or bash
, you can also use typeset
to get more information including attributes of variables, and list variables by type (like typeset -a
to list the array variables).
Here, by adding USER_ENVI=10
to your ~/.bashrc
, you're configuring the interactive non-login invocations of the bash
shell to define a USER_ENVI
shell variable on start-up. Since you've not used export
, that variable stays a shell variable (unless it was in the environment when bash
started), so it's not passed as environment variables to commands executed by that shell.
/etc/environment
, itself, on Ubuntu 16.04 is read by the pam_env.so
pluggable authentication module. Applications that log you in like login
, sshd
, lightdm
will read those files if configured to use pam_env.so
in /etc/pam.d
and pass the corresponding environment variables (nothing to do with shell variables here) to the command they start in your name after you authenticate (like your login shell for login
/sshd
, or your graphical session manager for lightdm
...).
Since the environment is inherited by default, when your session manager executes a terminal emulator which in turn executes your login shell, those environment variables will be passed along at each step, and your shell will map them to shell variables which you can expand in command line with things like echo "$VAR"
.
pam_env
env files like /etc/environment
look like shell scripts, but pam_env
doesn't invoke a shell to parse them and understands only a subset of the shell syntax and only allows defining variables whose name is made of one or more ASCII alpha-numeric characters or underscores (it does let you define a 123
variable though which is not a valid POSIX shell variable name).
¹, to pass a list of arbitrary env strings, you can also call execve()
directly like with:
perl -e 'require "syscall.ph";
$cmd = "/bin/zsh";
$args = pack("p*x[p]", "sh", "-c", "printenv");
$env = pack("p*x[p]", "a=b", "a=c", "", "+=+", "=foo", "bar");
syscall(SYS_execve(), $cmd, $args, $env)'
here testing with zsh
instead of bash
env
only shows inherited environment variables. It also sheds lights on the differenceshell variable
andenvironment variable
. Thank you very much – Tran Triet Sep 17 '18 at 12:51