8

You can trivially output your environment as a runnable shell script using

env > my_env

... later in another script ...

set -a
source my_env

This works for the most trivial cases, but fails if any special chars or spaces are in env.

How can I fix the above script so it works with stuff like a='"'"'" ?

Sam Saffron
  • 128,308
  • 78
  • 326
  • 506
  • 1
    Running that script doesn't do very much, though, given as as soon as it's done running it exits, and its parent's environment changes not a bit. `source my_env` makes more sense, but that doesn't require `chmod +x`. – Charles Duffy Nov 26 '14 at 01:03
  • good point removing the +x as its noise – Sam Saffron Nov 26 '14 at 01:07

3 Answers3

4

Use set instead of env:

○ → set | grep LESS
LESS=-R
LESSCLOSE='/usr/bin/lesspipe %s %s'
LESSOPEN='| /usr/bin/lesspipe %s'

○ → env | grep LESS
LESS=-R
LESSOPEN=| /usr/bin/lesspipe %s
LESSCLOSE=/usr/bin/lesspipe %s %s

Thanks to this similar question we can do this to get JUST environment variables as follows:

○ → (set -o posix; set) | grep LESS
LESS=-R
LESSCLOSE='/usr/bin/lesspipe %s %s'
LESSOPEN='| /usr/bin/lesspipe %s'

Try this behemoth which should print just the difference from a "pristine" shell. YMMV:

diff --old-line-format='%L' --unchanged-line-format= --new-line-format= \
    <(bash -c 'set -o posix; set' | sort) \
    <(env -i bash -c 'set -o posix; set' | sort)
Community
  • 1
  • 1
MikeyB
  • 3,288
  • 1
  • 27
  • 38
4

The following uses bash's printf %q to escape values correctly regardless of their content. It's guaranteed to handle literally any value possible -- quotes, newlines, etc -- so long as the shell sourcing its output is bash, and so long as the operating system in use supports the /proc/self/environ facility first provided by Linux to emit the contents of the environment as a NUL-delimited stream. It uses special quoting forms such as $'\n' as and where appropriate, so its output may not be honored by pure POSIX interpreters.

#!/usr/bin/env bash
while IFS= read -r -d '' kvname; do
  k=${kvname%%=*}
  v=${kvname#*=}
  printf '%q=%q\n' "$k" "$v"
done </proc/self/environ

Note that you'll want to source the output, not run it as an external executable, if you want your current shell's environment to change. If you don't want to set -a before sourcing, add a leading export to the format string.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • I just tried out (set -o posix; export) and it seems to do the trick as well, is this going to explode on me? – Sam Saffron Nov 26 '14 at 01:15
  • I doubt you need the `set -o posix` there, actually; `export` alone is probably fine. The one thing that the above code does better than the natively shell-based solutions is exposing environment variables that *aren't* also valid shell variables (for instance, when set by non-shell languages with non-shell-legal names), but the way those behave is... highly variable anyhow. – Charles Duffy Nov 26 '14 at 01:18
  • 1
    @SamSaffron, ...that said, reading the documentation for `export` (or, if you want to be more explicit, `export -p`), I don't see it _guaranteeing_ that its output is shell-safe. `printf '%q'` *guarantees* safety. On the other hand, relying on `/proc` is guaranteed to reduce your portability, so this approach definitely has its pitfalls, and the implementation in the versions of bash I'm looking at appear to be entirely safe despite not guaranteeing this in documentation. – Charles Duffy Nov 26 '14 at 01:21
  • @SamSaffron, why don't you add `export -p` as its own answer? You'd have my upvote. – Charles Duffy Nov 26 '14 at 03:23
  • k I added it, deployed that in my docker build earlier today and all seems happy. – Sam Saffron Nov 26 '14 at 03:50
3

Simplest solution seems to be

export -p > my_env
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506