Shih-Min Lee
Shih-Min Lee

Reputation: 9750

How to pass environment variables from a string to a bash command

I have a variable AAA which is in this format

AAA='BBB=1 CCC=2 DDD=3'

How can I use this to set environment variables BBB, CCC and DDD in a command I run (without permanently exporting them to the shell)? That is, I want to use to use the above to do something identical to:

# this works correctly: node is run with AAA, BBB, and CCC in its environment
BBB=1 CCC=2 DDD=3 node index.js

...however, when I try:

# this does not work: AAA=1 is run as a command, so it causes "command not found"
$AAA node index.js

...it tries to run BBB=1 as a command, instead of parsing the assignment as a variable to set in node's environment.

Upvotes: 3

Views: 5836

Answers (1)

Charles Duffy
Charles Duffy

Reputation: 295914

If you can, use a different format.

There are several better options:

  • An array.

    envvars=( AAA=1 BBB=2 CCC=3 )
    env "${envvars[@]}" node.js index.js
    
  • A NUL-delimited stream (the ideal format to use to save environment variables in a file -- this is the format your operating system uses for /proc/self/environ, for example).

    • Saving to a file:

      ```
      printf '%s\0' 'foo=bar' \
                    'baz=qux' \
                    $'evil=$(rm -rf importantDir)\'$(rm- rf importantDir)\'\nLD_LIBRARY_PATH=/tmp/evil.so' \
        > envvars
      ```
      
      ...or, even more simply (on Linux):
      
      ```
      # save all your environment variables (as they existed at process startup)
      cp /proc/self/environ envvars
      ```
      
    • Restoring from that file, and using it:

      ```
      mapfile -d '' vars <envvars
      env "${vars[@]}" node.js
      ```
      

But whatever you do, don't use eval

Remember that evil environment variable I set above? It's a good example of a variable that poorly-written code can't set correctly. If you try to run it through eval, it deletes all your files. If you try to read it from a newline-delimited (not NUL-delimited) file, it sets another LD_LIBRARY_PATH variable that tells your operating system to load a shared library from an untrusted/untrustworthy location. But those are just a few cases. Consider also:

## DO NOT DO THIS
AAA='BBB=1 CCC=2 DDD="value * with * spaces"'
eval $AAA node foo.js

Looks simple, right? Except that what eval does with it is not simple at all:

  • First, before eval is started, your parameters and globs are expanded. Let's say your current directory contains files named 1 and 2:

    'eval' 'BBB=1' 'CCC=2' 'DDD="value' '1' '2' 'with' '1' '2' 'spaces"' 'node' 'foo.js'
    
  • Then, eval takes all the arguments it's passed, and gloms them all together into a single string.

    eval "BBB=1 CCC=2 DDD="value 1 2 with 1 2 spaces" node foo.js
    
  • Then that string is parsed from the very beginning

    ...which means that if instead of having a file named 1 you had a file named $(rm -rf ~) (a perfectly valid filename!), you would have a very, very bad day.

Upvotes: 4

Related Questions