3

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.

14
  • 1
    I suggest: eval $AAA node index.js Commented Jan 5, 2020 at 2:47
  • 1
    Is there anything preventing you from storing the contents of AAA as an array, and then pass it as normal command parameters, like AAA=(BBB=1 CCC=2 DDD=3); node index.js "${AAA[@]}"? Commented Jan 5, 2020 at 2:54
  • 4
    eval introduces serious security and correctness issues; if your real command has more complex arguments it can easily corrupt them, and that's among the least harmful impacts. Consider env "${array[@]}" node index.js after setting up an array as @BenjaminW proposes earlier. Commented Jan 5, 2020 at 3:58
  • 1
    If you have the BSD or GNU versions of env, you can use env -S "$AAA" node index.js. Commented Jan 5, 2020 at 4:11
  • 1
    @root env $AAA node index.js subjects $AAA to pathname expansion, so there's a slight risk of not getting what you expect. A value like BBB=x -- y will also not work as expected due to the shell's word-splitting. -S lets env handle the splitting. Commented Jan 5, 2020 at 14:27

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

7 Comments

So the original process is you would have a .env file storing all the env variables. And then you source it and start your processes. The thing is I am using aws ssm get-parameters and all I get is a comma separated string and I don't know if I should try to parse it and store to the original .env format or not.
Ahh! That "real question" is probably a lot more tractable. Could you ask a separate question that includes example output from the aws command (ideally with enough detail to know that it does with values that contain commas, quotes, newlines, etc)?
(It's end of day where I'm at, but if you link that other question in, I'll look at it in the morning).
Ya I figured maybe I should do that. Lemme ask it in another post. Thanks!
@CharlesDuffy I'd recommend using defanged versions of the dangerous example strings, just in case someone tries them the wrong way. Something like $(rm /importantFile) or $(rm -R /yourHomeDirectory).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.