Environment variables of a running process on Unix?
I need to troubleshoot some problems related to environment variables on a Unix system. On Windows, I can use a tool such as ProcessExplorer to select particular a process and view values of each environment variable. How can I accomplish the same thing on Unix? echoing and env cmd just show values at present time, but I want to view what values the running process is using currently.
Even though /proc/
13 Answers 13
If you want to have pid(s) of a given running executable you can, among a number of other possibilities, use pidof :
AlberT$ pidof sshd 30690 6512
I totally quote Dennis Williamson and Teddy comments to achieve a more readable output. My solution is the following:
I don’t understand why using xargs to convert nulls to newlines is better than using tr . Can someone lay it out for me? Thanks.
Since this question has a unix tag and everyone else has done such a great job addressing linux tag, you can get this information on OS X and other BSD-derived systems using
Solaris also supports the /proc directory if you don’t want to remember the obscure ps commmand.
I I run ps -p -wwwe on OS X 10.6 I get the list of all running processes. the right command is with -E flag, not -e.
This is not perfect. The -E option only reports the initial environment variables. If the variables have been changed by the running process itself (e.g. through the use of the POSIX putenv() function), the changes are not reflected in the output of ps -p -wwE .
Not seeing env vars of processes not owned by you sounds like a desirable feature. It is also the case for @drAlberT’s Linux-oriented answer using /proc/PID/environ . Those files are only readable by the owner of the process.
@yani It’s possible, but it’s much harder, it requires attaching a debugger to the running process. See this answer: unix.stackexchange.com/a/70636
As others have mentioned, on Linux, you can look in /proc but there are, depending on your kernel version, one or two limits:
First of all, the environ file contains the environment as it looked when the process was spawned. That means that any changes the process might have made to its environment will not be visible in /proc:
$ cat /proc/$$/environ | wc -c 320 $ bash $ cat /proc/$$/environ | wc -c 1270 $
The first shell is a login shell and initially has a very limited environment but grows it by sourcing e.g. .bashrc but /proc does not reflect this. The second shell inherits the larger environment from the start, which it why it shows in /proc.
Also, on older kernels, the contents of the environ file is limited to a page size (4K):
$ cat /proc/$$/environ | wc -c 4096 $ env | wc -c 10343 $
Somewhere between 2.6.9 (RHEL4) and 2.6.18 (RHEL5) this limit was removed.
Linux environment variables of all processes
Is there similar command to list all environment variables from all processes? And is there one more similar command to list all values of specific environment variable from all processes?
Please note that the /proc//environ file only shows the env vars when the process was instantiated. It does not show the current env vars. That API is a great example of an anti-API; i.e., an API that should never have been created. Or at least should have been named something like initial-environ . See, for example, unix.stackexchange.com/questions/29128/….
1 Answer 1
To get a list of all process IDs, you can use this one-liner:
What this does, in three steps:
- ps -ef List process info of all processes running on the system, in standard format. Prints with a header row.
- awk » Print just the second section, splitting by whitespace. The format of ps -ef shows lines as UID , PID , PPID , C , STIME , TTY , TIME , and finally CMD . We only want the second one, so the awk command only gets the second column.
- tail -n +2 Cuts out the first line and only outputs the second line onwards. As mentioned previously, the ps -ef command has a header row, so we need to cut that for a bare list of PIDs.
To list all the environment variables for all of these PIDs, we can use the environ files as you mentioned. To do this, we’ll use a for loop with an evaluated command (between the backticks).
for pid in `ps -ef | awk '' | tail -n +2`; do cat /proc/$/environ; done;
for pid in `ps -ef | awk '' | tail -n +2`; do cat /proc/$/environ; done;
This for loop goes through that list of PIDs, assigning the variable pid to each iteration, and executes the cat command on each file with the PID in the path.
For readability, we can use strings or tr , both shown below.
for pid in `ps -ef | awk '' | tail -n +2`; do strings /proc/$/environ | cat; done; for pid in `ps -ef | awk '' | tail -n +2`; do tr '\0' '\n'
You could also use tools such as cut , sed , xargs , or anything else that gets the job done.
For finding specific environment variables, you can pipe all of that into grep instead of cat , which will automatically print to the console:
for pid in `ps -ef | awk '' | tail -n +2`; do strings /proc/$/environ | grep "SHELL"; done;
How to read environment variables of a process
You can read the initial environment of a process from /proc//environ .
If a process changes its environment, then in order to read the environment you must have the symbol table for the process and use the ptrace system call (for example by using gdb ) to read the environment from the global char **__environ variable. There isn't any other way to get the value of any variable from a running Linux process.
That's the answer. Now for some notes.
The above assumes that the process is POSIX compliant, meaning that the process manages its environment using a global variable char **__environ as specified in the Ref Spec.
The initial environment for a process is passed to the process in a fixed-length buffer on the process's stack. (The usual mechanism that does this is linux//fs/exec.c:do_execve_common(. ) .) Since the size of the buffer is calculated to be no more than the size required for the initial environment, you can't add new variables without erasing existing variables or smashing the stack. So, any reasonable scheme to allow changes in a process's environment would use the heap, where memory in arbitrary sizes can be allocated and freed, which is exactly what GNU libc ( glibc ) does for you.
If the process uses glibc , then it is POSIX compliant, with __environ being declared in glibc//posix/environ.c Glibc initializes __environ with a pointer to memory that it malloc s from the process's heap, then copies the initial environment from the stack into this heap area. Each time the process uses the setenv function, glibc does a realloc to adjust the size of the area that __environ points to to accommodate the new value or variable. (You can download the glibc source code with git clone git://sourceware.org/git/glibc.git glibc ). To really understand the mechanism you will also have to read the Hurd code in hurd//init/init.c:frob_kernel_process() (git clone git://git.sv.gnu.org/hurd/hurd.git hurd).
Now if the new process is only fork ed, without a subsequent exec overwriting the stack, then the argument and environment copying magic is done in linux//kernel/fork.c:do_fork(. ) , where the copy_process routine calls dup_task_struct that allocates the new process's stack by calling alloc_thread_info_node , which calls setup_thread_stack ( linux//include/linux/sched.h ) for the new process using alloc_thread_info_node .
Finally, the POSIX __environ convention is a user-space convention. It has no connection with anything in the Linux kernel. You can write a userspace program without using glibc and without the __environ global and then manage the environment variables however you like. No one will arrest you for doing this but you will have to write your own environment management functions ( setenv / getenv ) and your own wrappers for sys_exec and it is likely that no one will be able to guess where you put the changes to your environment.
How to show an environment variable's current value?
When I check on my system's environment, a lot of environmental variables will pop up. How can I just search for a particular variable? A book I'm reading says:
Sometimes the number of variables in your environment grows quite large, so much so that you don't want to see all of the values displayed when you are interested in just one. If this is the case, you can use the echo command to show an environment variable's current value.
5 Answers 5
For example for the environment variable $HOME , use:
Which then prints something similar to:
Edit: according to the comment of Stéphane Chazelas, it may be better if you use printenv instead of echo :
You forgot the quotes (unless you're implying zsh or rc/es syntax). echo is a poor choice of a command as it could transform the content of the variable. It will output the content of the shell parameter by the same name. That's not necessarily the same if using the Bourne shell or for env vars like 1 , * for instance. And you can't use that approach for env vars whose name is not valid as a shell variable name.
Also note that if there are several environment entries with the same name (OK, a pathological case), which one you'll get depends on the shell (usually either the first one or the last one). printenv VAR will display them all (at least for the GNU implementation).
It is not very clean to edit one's answer from a comment that was already made earlier under another more fitting answer ( printenv ) when it turns out that printenv and printenv VAR even seem the better way to go.
You will see all environment variables. For more info you can take a look at:
To come close to actually answering the question, printenv variablename will display only the named variable; e.g., printenv HOME will do approximately the same thing as echo "$HOME" .
It is important to understand that every process has its own set of environment variables.
When a process calls the fork() system call, a second process (the child) identical to the first (the parent) is created (this copy includes the environment, which resides just above the stack (or just below, depending how you think of stacks 🙂 - but in unix/linux the stack grows down from high addresses).
Usually, the child process will then call the execve() system call, which will throw away everything in its (virtual) memory and reconstruct it from the code and data sections in the specified binary file.
However, when it reconstructs the stack, it copies the environment and argument strings passed to execve() onto the stack first (in that order), before calling the main() function (a lot of the work is done in the crt0 bootstrap code after the execve() returns (to the entry point specified in the binary)).
There are wrappers for the execve() system call in the C library that will pass the current environment (i.e. a copy of the parents environment), instead of the caller providing it (so in effect the child will inherit the parent's environment) - see environ(7) .
Try running (as root) the command ps axeww | less . this will show you the environment for all processes! An interesting one is process id 1 (i.e. the init process - the first process created by the kernel at boot time).
If you want to look at the environment for a specific process (and you know it's process id), try running the command cat /proc//environ (replacing with the process id).
Note that if a process has enough privileges, it can rewrite its own stack, which can make it difficult to know what its environment is - you will see some daemon processes like this in the ps output.
But in the end, all this waffle boils down to what @chaos said above, if you want to look at the current value of a specific environment variable in your shell process, just use the (builtin) command echo "$" (replacing with the name of the environment variable you are interested in) . just be aware that the same variable may have a different value, or not exist at all, in another process.