tags:

views:

31

answers:

3

I'm trying to grovel through some other processes environment to get a specific env var.

So I've been trying a sed command like:

sed -n "s/\x00ENV_VAR_NAME=\([^\x00]*\)\x00/\1/p" /proc/pid/environ

But I'm getting as output the full environ file. If I replace the \1 with just a static string, I get that string plus the entire environ file:

sed -n "s/\x00ENV_VAR_NAME=\([^\x00]*\)\x00/BLAHBLAH/p" /proc/pid/environ

I should just be getting "BLAHBLAH" in the last example. This doesn't happen if I get rid of the null chars and use some other test data set.

This lead me to try transforming the \x00 to \x01's, which does seem to work:

cat /proc/pid/environ | tr '\000' '\001' | sed -n "s/\x01ENV_VAR_NAME=\([^\x01]*\)\x01/\1/p"

Am I missing something simple about sed here? Or should I just stick to this workaround?

+1  A: 

You could process the list with awk, setting the record separator to \0 and the field separator to =:

awk -v 'RS=\0' -F= '$1=="ENV_VAR_NAME" {print $2}' /proc/pid/environ

Or you could use read in a loop to read each NUL-delimited line. For instance:

while read -d $'\0' ENV; do declare "$ENV"; done < /proc/pid/environ

echo $ENV_VAR_NAME

(Do this in a sub-shell to avoid clobbering your own environment.)

John Kugelman
This also solves my issue with fumbling around with matching the first env var in the file not having a \x01 in front of it. Many thanks.
Trevor Harrison
+1  A: 

For some reason sed does not match \0 with .

% echo -n "\00" | xxd
0000000: 00                                       .
% echo -n "\00" | sed 's/./a/g' | xxd
0000000: 00                                       .
% echo -n "\01" | xxd                
0000000: 01                                       .
% echo -n "\01" | sed 's/./a/g' | xxd
0000000: 61                                       a

Solution: do not use sed or use your workaround.

unbeli
great troubleshooting steps, thanks.
Trevor Harrison
+1  A: 

A lot of programs written in C tend to fail with strings with embedded NULs as a NUL terminates a C-style string. Unless specially written to handle it.

I process /proc/*/environ on the command line with xargs:

xargs -n 1 -0 < /proc/pid/environ

This gives you one env var per line. Without a command, xargs just echos the argument. You can then easily use grep, sed, awk, etc on that by piping to it.

xargs -n 1 -0 < /proc/pid/environ | sed -n 's/^ENV_VAR_NAME=\(.*\)/\1/p'

I use this often enough that I have a shell function for it:

pidenv() 
{ 
    xargs -n 1 -0 < /proc/${1:-self}/environ
}

This gives you the environment of a specific pid, or self if no argument is supplied.

camh
very cool. I would have never thought to use xargs in this kind of role. However, I did avoid this type of solution in the beginning because I wanted to avoid any issues with env var values with \n in them.
Trevor Harrison
xargs allows you to easily handle multi-line values by running a script or program on each argument instead of just echoing it. This way, each argument can be processed independently knowing that each time your script is called, it is presented with a single value regardless of the number of lines in it.
camh