将环境变量从Makefile导出到用户环境

时间:2018-08-12 22:57:36

标签: makefile docker-compose

我正在寻找如何从Makefile环境变量中导出要在用户环境中公开的变量,因此应该可以从用户外壳程序中访问从Makefile中导出这些变量。

我尝试过make's export,但据我了解并尝试过,它不会导出到Makefile外部。

这样做的想法是用一种优雅的方式填充Docker Compose environment variables,并使这些变量也可以在用户shell中使用。

这是我尝试使用make export的片段:

include docker.env
export $(shell sed -n '/=/p' docker.env)

SHELL := /bin/bash

run:
    @docker-compose -f my-service.yml up -d

3 个答案:

答案 0 :(得分:1)

EDIT 在OP在注释中解释了他希望为 any 用户shell定义环境变量之后,完全重新设计了答案。

如果您的目标是为 any 用户外壳程序定义一组环境变量(我认为这是交互式外壳程序),则只需将这些定义添加到外壳程序的启动文件({{1 }}(用于bash)。从GNU制作手册:

  

make中的变量可以来自运行make的环境。   make在启动时看到的每个环境变量都是   转换为具有相同名称和值的make变量。   但是,在makefile中或使用命令进行显式分配   参数,覆盖环境。 (如果指定了“ -e”标志,   然后来自环境的值将覆盖makefile中的分配。   请参阅选项摘要。但这不是推荐的做法。)

示例:

.bashrc

如果要将这些定义分开,则可以仅从$ cat .bashrc ... export FOOBAR=foobar export BARFOO="bar foo" ... $ cat Makefile all: @printf '$$(FOOBAR)=%s\n' '$(FOOBAR)' @printf 'FOOBAR='; printenv FOOBAR @printf '$$(BARFOO)=%s\n' '$(BARFOO)' @printf 'BARFOO='; printenv BARFOO $ make $(FOOBAR)=foobar FOOBAR=foobar $(BARFOO)=bar foo BARFOO=bar foo 中获取文件:

.bashrc

最后,如果您不想在文件中添加$ cat docker.env export FOOBAR=foobar export BARFOO="bar foo" $ cat .bashrc ... source <some-path>/docker.env ... bash命令,则可以在export中解析文件:

.bashrc

当然,$ cat docker.env FOOBAR=foobar BARFOO="bar foo" $ cat .bashrc ... while read -r line; do eval "export $$line" done < <(sed -n '/=/p' <some-path>/docker.env) ... 文件的语法有一些限制(没有未用引号引起的特殊字符,变量名中没有空格,正确引用的值...)如果语法不兼容bash,则为是时候问另一个有关解析此特定语法并将其转换为bash兼容语法的问题了。

答案 1 :(得分:1)

根据ArchWiki,Bash的每个进程...

  

每个进程将其环境存储在/ proc / $ PID / environ文件中。

因此,一旦使执行sourceexport或任何其他命令来设置新的环境变量,它将仅应用于该进程。

作为解决方法,我已经在bash启动文件中编写了代码,这样一来,一旦加载了新的bash shell,这些变量便会进入全局环境:

SHELL := /bin/bash
RC := ~/.bashrc
ENV := $(shell sed -n '/=/p' docker.env)

test:
     @$(foreach e,$(ENV),echo $(e) >> $(RC);) \

答案 2 :(得分:0)

没有合作伙伴,Make不能更改调用Shell的环境。当然,如果您处于控制之中,则可以 make 调用shell进行配合。

广义上,您可以用运行真实make的shell别名或函数替换make命令,并根据结果设置环境变量。我将继续更详细地描述实现此目标的一种方法。

您是否将此别名或函数称为make,例如compose完全取决于您。要包装实际的make会稍微困难一些-在函数内部,您需要说command make,因为仅make会导致别名或函数递归调用自身的无限循环-所以我将演示这一点。让我们定义一个函数(别名吮吸);

make () {
    # run the real make, break out on failure
    command make "$@" || return
    # if there is no env for us to load, we are done
    test -f ./docker.env || return 0
    # still here? load it
    . ./docker.env
}

如果您想要更严格的控制,可以在函数中定义一个变量,然后在Makefile内部检查是否已设置该变量。

$(ifneq '${_composing}','function_make')
    $(error Need to use the wrapper function to call make)
$(endif)

如果您还没有阅读此讨论,则错误消息会令人困惑,因此可能需要改进和/或将其记录在自述文件中。您可以将上面函数中的make行更改为

    _composing='function_make' \
    command make "$@" || return

语法var=value cmd args仅在运行命令行var的持续时间内将变量value设置为字符串cmd args;然后,它返回到其先前的状态(未设置或设置为其先前的值)。

对于这种特殊的构造,变量名只需要合理地唯一且对好奇的读者透明即可;并且该值也是函数和Makefile需要达成共识的合理唯一且合理透明的字符串。

取决于最终存储在环境中的内容,如果您需要针对多个Makefile的这种机制,可能会带来复杂性。在目录a中运行它,然后切换到类似的目录b似乎可以工作,但是使用了a的东西,可怜的人会期望b的东西。 (如果您设置的变量包含路径,则相对路径可以解决此问题,但会使其他情况复杂化。)

将此扩展到类似于Ruby的rvm或Python的virtualenv的模型可能值得探索;它们通常会在shell提示中添加一个指示符,以提醒您当前哪个环境处于活动状态,并具有一些(非常适度的)保护措施来在当前目录和环境不一致时向您发出警告。

另一种缺陷:始终总是加载make的硬编码docker.env可能会在一天中产生不受欢迎的意外。也许硬编码一个特定于该钩子的不同文件名-例如.compose_post_make_hook?然后,它可以包含类似

的内容
. ./docker.env

在此特定目录中。