2

We currently have a command for setting the environment for only the following arguments/commands. The environment variables are set by a file, through a simple cat command:

env $(cat "$1") <rest of the command>

However, shellcheck seems to mark this as "Quote this to prevent word splitting.shellcheck(SC2046)".

What would be the best way of handling this error, as I am not sure if I am correct by simply ignoring it for this particular case? As I am aware there are multiple routes to take (using xargs, following principle of "useless cat", etc).

Example environment file:

VARIABLE_1=TEST
VARIABLE_2=TEST2

Update

I am hoping for some consensus among the community on how to write such .env files and how to read those files. The file, nor the interpreter should do any more magic tricks than if we would this inline before a command.

nkmol
  • 8,025
  • 3
  • 30
  • 51
  • Why don't you precede each variable declaration in the file with `export ` and just source it (possibly in a subshell for keeping the current env clean)? – oguz ismail Dec 16 '19 at 09:52
  • The error could be a bit too trivial for your use case. Quoting the entire substitution should get rid of that warning. But would be interesting to see your file contents – Inian Dec 16 '19 at 09:52
  • @Inian if there are multiple declarations in the file, that would mess up the environment. Just try it and see – oguz ismail Dec 16 '19 at 09:54
  • There really *isn't* a good (or at least fool-proof) solution to this. At the very least, though, you need to precisely define exactly what the file can contain. Can it contain any valid shell assignment (whose values can contain embedded newlines), or is it more constrained? – chepner Dec 16 '19 at 15:03
  • 1
    Both GNU `env` (null-terminated strings) and BSD `env` (escape processing and splitting) have options for dealing with this, though both require some pre-processing of the file. – chepner Dec 16 '19 at 15:04
  • @chepner I think we can limit the content to assignments with one-line. I am not sure about any other limitations. I except we also do not want to support any spaces between assignments (`VARIABLE_3=TEST MY ASSIGNMENT`, should equal to `TEST`). I was hoping for some consensus on how to write such `.env` files and how to read those files. – nkmol Dec 16 '19 at 18:40
  • 1
    The only **right** thing to do is to NUL-delimit your files, making their content identical to what you get from reading `/proc/self/environ` on Linux. Otherwise, not all environment variables can be represented in the content (particularly, ones containing literal newlines will simply come out wrong). – Charles Duffy Dec 16 '19 at 18:56
  • 1
    Consider what happens if you try to create and use a newline-delimited file after someone runs `export 'my_harmless_var='$'\n''LD_LIBRARY_PATH=/tmp/evil.so'`; what had been a harmless variable becomes a malicious one. And that's ignoring cases where you just corrupt harmless variables that intentionally contain newlines because they contain code to run or whatnot. – Charles Duffy Dec 16 '19 at 18:58

1 Answers1

2

Shellcheck is (as usual) correct to complain about the lack of quotes. The code will break if values have embedded spaces, and may break if they contain shell globbing characters.

Adding quotes (env "$(< "$1")" ...) doesn't work. It will only set a single environment variable with embedded linefeeds which will make env output look cosmetically correct, but set and similar will show that the values are wrong

One safe, and Shellcheck-clean, way to do it is to read the lines of the file into an array and pass each of the lines as a separate argument to env:

readarray -t settings <"$1"
env "${settings[@]}" ...
pjh
  • 6,388
  • 2
  • 16
  • 17
  • The first solution makes a *lot* of assumptions about the strings you read from the file; the second makes fewer, but still some. – chepner Dec 16 '19 at 15:01
  • What does `<` mean in `$(< "$1")`? – Vadiem Janssens Dec 16 '19 at 15:23
  • @VadiemJanssens, see [Command Substitution (Bash Reference Manual)](https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html). `$(< file)` is similar to `$(cat file)` but it's faster (because it doesn't create a subprocess) and it works with unusual filenames (particularly ones that begin with `-`, including a file called just `-`). – pjh Dec 16 '19 at 16:05
  • 1
    `env "$(< "$1")" ...` doesn't work. It will only set a single environment variable with embedded linefeeds which will make `env` output look cosmetically correct, but `set` and similar will show that the values are wrong – that other guy Dec 18 '19 at 17:57
  • @thatotherguy, thanks for reporting my mistake. I was fooled by the cosmetic correctness. I've updated the answer. – pjh Dec 18 '19 at 21:18