somevar := apple
export somevar
update := $(shell echo "v=$$somevar")
all:
@echo $(update)
我希望苹果作为命令的输出,但它是空的,这使我认为导出和:=
变量扩展发生在不同的阶段。怎么克服这个?
答案 0 :(得分:32)
问题是export
将变量导出到命令使用的子shell;它不适用于其他作业的扩展。因此,不要试图从规则之外的环境中获取它。
somevar := apple
export somevar
update1 := $(shell perl -e 'print "method 1 $$ENV{somevar}\n"')
# Make runs the shell command, the shell does not know somevar, so update1 is "method 1 ".
update2 := perl -e 'print "method 2 $$ENV{somevar}\n"'
# Now update2 is perl -e 'print "method 2 $$ENV{somevar}\n"'
# Lest we forget:
update3 := method 3 $(somevar)
all:
echo $(update1)
$(update2)
echo $(update3)
perl -e 'print method 4 "$$ENV{somevar}\n"'
答案 1 :(得分:14)
@Beta's answer包含关键指针:使用GNU make
,标有export
的变量仅适用于[为配方命令启动的shell (属于规则的命令),令人遗憾的是不是$(shell ...)
的调用(他们只看到make
在启动时看到的环境)
有一个解决方法,但是: 显式将导出的变量作为环境变量传递给shell
函数:< / p>
update := $(shell somevar='$(somevar)' perl -e 'print "$$ENV{somevar}"')
通过在<var>=<val>
前加上shell命令,该定义将作为环境变量添加到命令所看到的环境中 - 这是一个通用的shell功能。
警告:@Kaz在评论中指出,如果$(somevar)
包含某些字符,此方法会出错,因为变量扩展是逐字(没有转义),它可以破坏生成的shell命令,并建议以下变体也适用于嵌入式'
实例(将输入值分解为带引号'
的单引号子串拼接):
update := $(shell somevar='$(subst ','\'',$(somevar))' perl -e 'print "$$ENV{somevar}"')
这应该适用于除多行之外的所有值(这种情况很少见;对于我不知道的多行值没有解决方法)。
旁注,文字$
字符。 in值必须表示为$$
,否则make
会将它们解释为对自己变量的引用。
请注意,我故意不选择OP的原始语句update := $(shell echo "v=$$somevar")
进行演示,因为它包含了一个陷入困境的陷阱:由于shell的原因评估命令行,somevar=apple echo v=$somevar
不评估为v=apple
,因为{/ 1}}参考在 $somevar
生效之前已展开。要在这种情况下达到预期效果,您必须使用 2 语句:somevar=apple
功能
关于错误与特征的争论:
虽然可以认为update := $(shell export somevar="$(somevar)"; echo "v=$$somevar")
函数应该看到与配方命令相同的环境,但文档没有做出这样的承诺 - 请参阅http://www.gnu.org/software/make/manual/make.html#Shell-Function。相反,http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion仅提及将导出变量提供给 recipe 命令。
答案 2 :(得分:10)
运行makefile
foo:=apple
export foo
all:
@echo ">"$(shell echo "$$foo")
@echo ">""$$foo"
给我(在环境中未定义foo)
$ make
>
>apple
$ make foo=bar
>
>apple
$ export foo=bar; make
>bar
>apple
$ export foo=bar; make foo=bar
>bar
>bar
尝试使用引用的表单(update := "v=$$somevar"
)并在运行命令时让shell处理扩展(您仍需要导出)
答案 3 :(得分:1)
虽然export
与$(shell ...)
无法很好地协作,但有一个简单的解决方法。我们可以通过命令行将数据传递给shell脚本。
当然,环境通过对于逃避和引用的问题是强有力的。但是,shell语言有一个单引号引用方法'...'
来处理所有内容。唯一的问题是没有办法在那里得到单一的报价;但当然这可以通过终止引用,反斜杠 - 转义所需的单引号并开始一个新引用来解决:换句话说:
ab'cd -> 'ab'\''cd'
在$(shell ...)
执行的shell脚本中,我们只生成var='$(...)'
形式的变量赋值,其中$(...)
是一些make表达式,用于插入适当的转义素材。因此,Makefile
:
somevar := apple with 'quoted' "stuff" and dollar $$signs
shell_escape = $(subst ','\'',$(1))
update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v > file.txt)
.phony: all
all:
cat file.txt
示例运行:
$ make
cat file.txt
apple with 'quoted' "stuff" and dollar $signs
如果我们想要将环境变量传递给命令,我们可以使用shell语法VAR0=val0 VAR1=val1 ... VARn=valn command arg ...
来实现。这可以通过上述Makefile
的一些小改动来说明:
somevar := apple with 'quoted' "stuff" and dollar $$signs
shell_escape = $(subst ','\'',$(1))
update := $(shell somevar='$(call shell_escape,$(somevar))' env > file.txt)
.phony: all
all:
grep somevar file.txt
执行命令
$ make
grep somevar file.txt
somevar=apple with 'quoted' "stuff" and dollar $signs
file.txt
包含一系列环境变量,我们可以看到somevar
。如果GNU Make中的export
做了正确的事情,我们就能做到:
export somevar
update := $(shell env > file.txt)
但最终结果是一样的。
由于您想要的最终结果是echo $(update)
,所以即使GNU Make将导出的变量传递给shell_escape
,您也会$(shell ...)
。也就是说,再看一个Makefile
:
somevar := apple with 'quoted' "stuff" and dollar $$signs
shell_escape = $(subst ','\'',$(1))
update := $(shell v='$(call shell_escape,$(somevar))'; echo $$v)
.phony: all
all:
@echo '$(call shell_escape,$(update))'
@echo $(update)
输出:
apple with 'quoted' "stuff" and dollar $signs
apple with quoted stuff and dollar