【问题标题】:How to load and export variables from an .env file in Makefile?如何从 Makefile 中的 .env 文件加载和导出变量?
【发布时间】:2017-11-21 12:44:18
【问题描述】:

在 Makefile 中使用.env 的最佳方式是什么,即加载该文件并导出make 中子shell 的所有变量?

如果提议的解决方案仅适用于make,那就太好了,例如不使用任何第三方工具。 .env 文件还支持多行变量,例如:

FOO="this\nis\na\nmultiline\nvar"

这就是为什么this solution 可能不够用。

【问题讨论】:

  • include 有效。
  • 并非如此。在大多数情况下,它可能会正确加载变量,但它不会自动将它们导出为子 shell 中命令的环境,对吧?
  • 如果您在谈论 GNU make,可能有一些非常复杂的方法可以使用字符串函数和$(eval ) 来实现这一点,但听起来不像好主意。如果您要通过导出这些变量来说明您想要实现的目标,那么可能会有一个更直接的解决方案...
  • 这个想法是遵循 12 因素应用程序的想法,有一个 .env 文件,其中包含构建/部署配置所需的所有环境变量。由于make 是协调构建/部署的工具,如果我可以在没有外部工具的情况下轻松使用.env,那就太好了。

标签: makefile environment-variables


【解决方案1】:

Make 不提供将文件内容读取到某个变量的任何方法。所以,我认为不使用外部工具是不可能达到这个结果的。不过,如果我错了,我很乐意学习一些新技巧。

所以,我们假设有两个文件,.env,是一个技术上正确的 shell 文件:

FOO=bar

BAR="notfoo" # comment
   #comment
MULTILINE="This\nis\nSparta!"
# comment

script.sh:

#!/bin/bash
echo FOO=${FOO}
echo BAR=${BAR}
echo -e ${MULTILINE}

一种解决方案是包含.env 文件,然后确保已导出变量:

include .env

$(eval export $(shell sed -ne 's/ *#.*$$//; /./ s/=.*$$// p' .env))

all:
    ./script.sh

由于shell和make对引号的处理不同,你会在输出中看到引号。

您可以通过 make 重新处理变量来避免这种情况:

include .env

VARS:=$(shell sed -ne 's/ *\#.*$$//; /./ s/=.*$$// p' .env )
$(foreach v,$(VARS),$(eval $(shell echo export $(v)="$($(v))")))

all:
    ./script.sh

但随后多行变量将变为单行。

最后,你可以生成一个临时文件,由 bash 处理,并在运行任何命令之前获取它:

SHELL=bash

all: .env-export
    . .env-export && ./script.sh

.env-export: .env
    sed -ne '/^export / {p;d}; /.*=/ s/^/export / p' .env > .env-export

哦,在这种情况下,多行变量中的新行弄乱了。您需要另外引用它们。

最后,您可以使用上面的sed 命令将export 添加到.env,然后:

SHELL=bash
%: .env-export
    . .env-export && make -f secondary "$@"

【讨论】:

  • 好吧,我想你说服了我使用外部工具。感谢您的详细回答!
  • shell 和 make 对引号的整个处理让它变得非常混乱。另一种解决方案似乎是在每个命令前面加上. .env; ,即. env; echo $FOO
  • 在您给出的示例中,$FOO 由相同的外壳采购.env 文件扩展。要运行 . .env; ./script.sh 变量需要已经导出到 .env 文件中,以便在 ./script.sh 中可见。
【解决方案2】:

您可以通过创建函数和目标来为每个目标加载特定的 .env 文件,以便在必要时将其与其他目标一起使用。这里作为示例:

define setup_env
    $(eval ENV_FILE := $(1).env)
    @echo " - setup env $(ENV_FILE)"
    $(eval include $(1).env)
    $(eval export)
endef

devEnv: 
    $(call setup_env, dev)

prodEnv: 
    $(call setup_env, prod)

clean:
    rm -rf  bin/

build: clean
    GOOS=linux GOARCH=amd64 go build -o bin/ ./cmd/...

dev: build devEnv
    cd cmd/api && ./../../bin/api
 
migrate-dev-db: devEnv
    sh +x database/migration/migrate.sh dev

migrate-prod-db: prodEnv
    sh +x database/migration/migrate.sh


deploy: prodEnv
    sh +x script/deployment/production/ec2-deploy.sh

【讨论】:

  • 为什么dev目标依赖devEnv,然后也立即调用$(call setup_env,dev)
  • @MarkLakata 对不起这个错误。我编辑了我的答案。
【解决方案3】:

找到了,效果很好:

在makefile的顶部

ifneq (,$(wildcard ./.env))
    include .env
    export
endif

然后你为你的所有环境创建变量,例如 MY_VAR 用作 $(MY_VAR)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-11
    • 2020-09-24
    • 2021-10-14
    • 2021-01-13
    相关资源
    最近更新 更多