Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

If I examine /proc/1/environ I can see a null-byte-delimited string of process 1's environment variables. I'd like to bring these variables into my current environment. Is there an easy way to do this?

The proc man page gives me a snippet which helps be print out each environment variable on a line-by-line basis (cat /proc/1/environ; echo) | tr '\000' '\n'. This helps me verify the contents are correct, but what I really need to do is source these variables into my current bash session.

How do I do that?

share|improve this question
add comment

6 Answers

The following will convert each environment variable into an export statement, properly quoted for reading into a shell (because LS_COLORS, for example, is likely to have semicolons in it), then sources it.

[The printf in /usr/bin, unfortunately, generally doesn't support %q, so we need to call the one built into bash.]

. <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ)
share|improve this answer
    
I suggest . <(xargs -0 bash -c 'printf "export %q\n" "$@"' -- < /proc/nnn/environ), which will handle variables with quotes in them properly as well. –  John Kugelman 15 hours ago
    
@JohnKugelman Thanks very much for the improvement, using "$@" instead of '{}'. For those wondering about the -- argument in his improved answer: positional arguments to bash -c command_string are assigned starting at $0, while "$@" expands to include arguments starting at $1. The argument -- gets assigned to $0. –  Mark Plotnick 13 hours ago
add comment

In this answer, I assume a system where /proc/$pid/environ returns the environment of the process with the specified PID, with null bytes between variable definitions. (So Linux, Cygwin or Solaris (?)).

Zsh

export "${(@ps:\000:)$(</proc/$pid/environ)}"

(Pretty simple as zsh goes: an input redirection with no command <FILE is equivalent to cat FILE. The output of the command substitution undergoes parameter expansion with the flags ps:\000: meaning “split on null bytes”, and @ meaning “if the whole thing is in double quotes then treat each array element as a separate field” (generalizing "$@").)

Bash, mksh

while IFS= read -r -d "" PWD; do export "$PWD"; done </proc/$pid/environ
PWD=$(pwd)

(In these shells, an empty delimiter passed to read results in null bytes being separators. I use PWD as a temporary variable name to avoid clobbering another variable that might end up being imported. While you could technically import PWD as well, it would only stay put until the next cd.)

POSIX

POSIX portability isn't that interesting for this question, because it only applies to systems that have /proc/PID/environ. So the question is what Solaris sed supports — or whether Solaris has /proc/PID/environ, it didn't use to but I'm way behind the curve on Solaris features so it might nowadays. On Linux, GNU utilities and BusyBox are both null-safe, but with caveates.

If we do insist on POSIX portability, none of the POSIX text utilities are required to handle null bytes, so this is difficult. Here's a solution that assumes that awk supports a null byte as the record delimiter (nawk and gawk do, as does BusyBox awk, but mawk doesn't).

eval $(</proc/$pid/environ awk -v RS='\0' '{gsub("\047", "\047\\\047\047"); print "export \047" $0 "\047"}')

BusyBox awk (which is the version commonly found on embedded Linux systems) does support null bytes but not setting RS to "\0" in a BEGIN block and not the command line syntax above; however it does support -v 'RS="\0"'. I haven't investigated why, this looks like a bug in my version (Debian wheezy).

(Wrap all lines null-separated records in single quotes "\047", after escaping the single quotes inside values.)

Caveats

Beware that any of these might attempt to set read-only variables (if your shell has read-only variables).

share|improve this answer
    
Neither print nor $(<file) are applications you can rely upon in a POSIX environment. –  mikeserv yesterday
    
Now that is an excellent point. I didnt even consider it. You tend to think about that standard in ways i dont - though i should. Im always thinking about how the shell is affected because im always considering my minimum dependencies where init would be concerned, but, of course, thats not really what its about, is it? –  mikeserv 23 hours ago
    
@mikeserv I'm not sure what you meant by your comment, seeing as I don't use print (except in an awk script, and print is most definitely in the POSIX specification for awk). The $(<file) feature is a zsh feature, which I use in the zsh section. –  Gilles 23 hours ago
    
Oh my goodness. Sorry about that. I screwed that up. I thought it was zsh by then. Please accept my apology. I use the $(<file) thing too, honestly. It was just a warning to othera. Obviously you know what it does. –  mikeserv 23 hours ago
add comment

In bash you can do the following. This will work for all possible contents of the variables and avoids eval:

while IFS= read -rd '' var; do declare +x "$var"; done </proc/$PID/environ

This will declare the read variables as shell variables in the running shell. To export the variables into the running shell environment instead:

while IFS= read -rd '' var; do export "$var"; done </proc/$PID/environ
share|improve this answer
add comment

Using source and process substitution:

source <(sed -r -e 's/([^\x00]*)\x00/export \1\n/g' /proc/1/environ)

Shortly:

. <(sed -r -e 's/([^\x00]*)\x00/export \1\n/g' /proc/1/environ)

Using eval and command substitution:

eval `sed -r -e 's/([^\x00]*)\x00/export \1\n/g' /proc/1/environ`

The sed call can be replaced with an awk call:

awk -vRS='\x00' '{ print "export", $0 }' /proc/1/environ

But don't forget that it doesn't clear any environment variables that are not in pid 1.

share|improve this answer
    
Is the export redundant in @fr00tyl00p's answer? Cause if not it seems pretty important –  thedeeno yesterday
    
Yes, the export is needed. I'll fix it up. –  Pavel Šimerda yesterday
2  
All of these commands choke on values that contain newlines and (depending on the command) other characters. –  Gilles yesterday
add comment

I think this is POSIX portable:

. <<ENV /dev/stdin
    $(sed -n 'H;${x;s/\(^\|\x00\)\([^=]*.\)\([^\x00]*\)/\2\x27\3\x27\n/gp}' \
       /proc/$pid/environ)
ENV

But @Gilles makes a good point - sed will probably handle nulls, but maybe not. So there's this (I Really think so this time) actually POSIX portable method, too:

s=$$SED$$
sed 's/'\''/'$s'/;1s/^./'\''&/' </proc/"$$"/environ |
tr '\0' "'" |
sed 's/'\''/&\n&/g' |
sed '1d;$d;s/^\('\''\)\([^=]*.\)/\2\1/;s/'$s'/'\\\''/g'

Still, if you've got GNU sed you need only do:

sed -z 's/^[^=]*./&'\''/;s/$/'\''\n/' </proc/"$$"/environ

  #BOTH METHODS OUTPUT:

enter image description here

Well, POSIX portable that is except for the /dev/... which is not specified but you can pretty much expect that syntax to behave same on most Unices.

Now if this has anything to do with your other question, you might like to use it like this:

nsenter -m -u -i -n -p -t $PID /bin/bash 5<<ENV --rcfile=/dev/fd/5 
    $(sed -z 's/^[^=]*./&'\''/;s/$/'\''\n/' </proc/"$$"/environ)
ENV

The here-doc is extremely helpful in that it keeps the shell from screwing with any of the quoting we work so hard to handle in the subshell and also provides us a reliable path to a .dot sourceable file rather than, again, a subshell or a shell variable. Others here use the <(process substitution) bashism which works in much the same way - only it's definitely an anonymous |pipe whereas POSIX only specifies an iohere for here-docs and so it can be any type of file, though, in practice, it's usually a temp file. (dash, on the other hand, does use anonymous |pipes for here-docs). The unfortunate thing about process substitution though, is it's also shell dependent - which might be an especially annoying issue if you're working with init.

This also works with |pipes of course, but then you lose the environment again in the end when the |pipe's state evaporates with its subshell. Then again, this works:

sed '...;a\exec <>/dev/tty' /proc/$pid/environ | sh -i 

The sed statement itself works by holding every line in memory until it reaches the last, at which time it performs a global replace handling quoting and inserting newlines where appropriate by anchoring on the nulls. Fairly simple really.

In the dash picture you'll see I opted to eschew the \ mess and added the GNU specific -r option to sed. But that's just cause it was less to type. It works either way, as you can see in the zsh image.

Here's zsh:

enter image description here

And here's dash doing the vary same thing:

enter image description here

Even terminal escapes come through unscathed:

enter image description here

share|improve this answer
    
This is not POSIX-portable because sed is not required to handle null bytes. (That being said, POSIX portability isn't that interesting for this question, because it only applies to systems that have /proc/PID/environ. So the question is what Solaris sed supports — or whether Solaris has /proc/PID/environ, it didn't use to but I'm way behind the curve on Solaris features so it might nowadays.) –  Gilles 23 hours ago
    
@Gilles No. But sed is required to handle ascii hexadecimal, of which the null byte is one. Besides, i actually just thought if a much easier way to do this still. –  mikeserv 23 hours ago
    
No, POSIX says that “The input files shall be text files” (for sed and other text utilities) and defines text files as “file that contains characters organized into one or more lines. The lines do not contain NUL characters (…)”. And by the way the \xNN syntax is not required in POSIX, not even the \OOO octal syntax (in C strings and in awk, yes, but not in sed regexps). –  Gilles 23 hours ago
    
@Gilles you've got a point. I looked all over and I couldn't find what I thought I could before. So I did it differently. Editing now. –  mikeserv 19 hours ago
    
As far as I can tell, Solaris doesn't have /proc/PID/environ after all (it has several other Linux-like entries in /proc/PID, but not environ). So a portable solution doesn't need to go beyond Linux tools after all, meaning GNU sed or BusyBox sed. Both do support \x00 in a regexp, so your code is as portable as needed (but not POSIX). It's overly complex though. –  Gilles 19 hours ago
show 3 more comments
eval \`(cat /proc/1/environ; echo) | tr '\000' '\n'\`
share|improve this answer
    
This doesn't work if any value contains any shell special character. –  Gilles yesterday
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.