printenv are both external commands i.e. executable files, not bash builtins.
The problem with them is that some environment variables (such as
_, and I also wonder if there are more) which they output don't belong to the shell which invokes them, see here.
$ declare -x declare -x ALTERNATE_EDITOR="" declare -x COLORFGBG="7;0" declare -x COMMAND_MODE="unix2003" ... declare -x VISUAL="gvim"
It will not list any local variables that are not exported, but will include variables inherited from the shell's outer environment.
declare -x -F -p will omit any exported functions.
Note the wording in the Bash manual: "all variables marked for export". What we think of the "environment" of a process is what's passed in the
envp parameter of the
execve system call, and only what is passed via this parameter. The rest is just an illusion. The calling process (a shell or any other type of process) can do whatever it wants with the received variables, according to how it is documented to behave. It can pass the variables it received to a subprocess, or it can choose not to.
What I am aiming at with all this is that there is no magical environment variable block that just automatically gets passed in the shades from a parent process to its children. The parent process can do what it wants with its own "environment", hack it to pieces, encrypt it in memory, copy it to some other address in the process space (which Bash does), etc. There can not be any external mechanism, nor a kernel mechanism, to inspect the environment of a process (wait – read the comment on
/proc/PID/environ below). The only thing that matters is that the exporting process should collect the pieces and pass an environment to its child processes according to its own rules; in the case of Bash, the rules are listed in the Bash manual.
What this means is that the file
/proc/$$/environ is deceptive. It does not reflect any changes to the shell's environment, but just reports what the shell received when it was
execed by the parent process.
No, there is not.
There are three sets of "variables" here.
- The Bourne Again shell inherits a set of environment variables from whatever parent process forked and executed it. These remain the environment variables of the Bourne Again shell process throughout its lifetime.
- These environment variables initialize the second set, the shell variables that the Bourne Again shell lets users see and manipulate with commands such as
- This second set of shell variables determines, by dint of which shell variables are marked as exportable (alongside a couple of other mechanisms, outwith the scope of this answer), what environment variables child processes forked by the Bourne Again shell themselves inherit after
execveto run an external command in that child process.
The first set are "the environment variables of the current shell". The second set are shell variables, and the third set are environment variables of a child process.
Although it is possible for the Bourne Again shell to alter its process environment variables, and make that alteration visible to other processes, as I explained programs can do at https://unix.stackexchange.com/a/432681/5132, in practice the Bourne Again shell does not do this.
Similarly, it is possible for the Bourne Again shell to provide access to its environment variables, but it does not, only providing access to the shell variables derived from those environment variables at shell program startup.
(I mention the Bourne Again shell because of the question, but in fact most shells have similar behaviour and this isn't really specific to the Bourne Again shell.)
And of course external programs like
printenv display the third set of variables.
To see the environment variables of the current shell process, one has to use an external command:
ps ewwww -p $$or
hexdump -C /compat/linux/proc/$$/environ(just
/procon Linux operating systems)