2

I have a bash function that retrieves environment variables from a .env file.

In my .env file I have the following variables

USER=luis
IMAGE_NAME=application
IMAGE_VERSION=latest
IMAGE_TAG=${USER}/${IMAGE_NAME}:${IMAGE_VERSION}

In my bash script I have my env function

function dotenv::get() {
  variable=$(grep ^"${1}"= "$(pwd)/.env" | xargs)
  IFS="=" read -ra variable <<< "${variable}"

  echo "${variable[1]}"
}

When I execute dotenv::get IMAGE_TAG.

Expected result: luis/application:latest

Current result: ${USER}/${IMAGE_NAME}:${IMAGE_VERSION}

I'm aware my function is incomplete, however I'm not sure what are the next steps to achieve my objective.

5
  • | xargs what is that xargs doing there? Commented Jan 25, 2021 at 20:42
  • Is : legal in function names? I'd be surprised. Commented Jan 25, 2021 at 20:53
  • @KamilCuk Probably to get input from multiple lines onto a single line. Commented Jan 25, 2021 at 20:54
  • @BenjaminW. I'm following Google Shell Style Guide google.github.io/styleguide/shellguide.html#s7.1-function-names Commented Jan 25, 2021 at 20:56
  • 1
    Ah, indeed. Seems to be okay in Bash, and allowed in POSIX. Commented Jan 25, 2021 at 21:04

2 Answers 2

3

The file looks like it wants to keep shell-ish syntax. If so, just source it:

. ./.env
echo "$IMAGE_TAG"

dotenv::get() { 
    . ./.env;
    echo "${!1}"
}

If this is not your intention, then implement a whole parser of variables from a file with a ${...} variable substitution of already set variables.

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

10 Comments

Sourcing the file would also set the variables, which is probably not what the user wants. You could (in a child process) source the file and do the echo, but then only exported variables would be expanded. I fear to get the functionality the OP asks for, there is no other way than doing an eval, with all of its perils (which, of course, would exist if you source the file).
Sure, so do dotenv::get() { ( . ./.env; echo "${!1}" ); }
This would expand only those unexported variables, which are defined in .env, but not those which are defined in the process which invokes this function. This may or may not be what the OP wants. See my comment to the question, which talks about this issue.
I do not understand. No, all variables are available, . is executed in current shell. No, subshells executed by ( ) share all variables, not only exported ones. And, the .env file is self-sufficient - it does not reference any variables that are not set within it itself.
Is this the semantics of the (....) construct Yes, of a "subshell", so also $(...) and backticks and pipelines. Let's see posix shell execution environment see A subshell environment shall be created as a duplicate of the shell environment stuff.
|
2

You are still missing two things: interpreting your variable assignments, and interpreting the content of the variable you query:

. $(pwd)/.env  # source the environment file so that variables are assigned

function dotenv::get() {
  variable=$(grep ^"${1}"= "$(pwd)/.env" | xargs)
  IFS="=" read -ra variable <<< "${variable}"

  eval X=${variable[1]}  # interpret the expression containing variables
  echo $X
}

dotenv::get IMAGE_TAG

3 Comments

It's better to do declare "X=${variable[1]}", then eval with unquoted expansion.
Wouldn't it be better to use the indirection operator ${!variablename}?
Why do you need to interpret the expression when it was interpreted by .?

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.