somevar := apple
export somevar
update := $(shell echo "v=$$somevar")
all:
@echo $(update)
我希望命令的输出结果是apple,但是它是空的,这让我觉得export和:=
变量扩展发生在不同的阶段。如何解决这个问题?
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"'
输出结果为:echo method 1
method 1
perl -e 'print "method 2 $ENV{somevar}\n"'
method 2 apple
echo method 3 apple
method 3 apple
perl -e 'print "method 4 $ENV{somevar}\n"'
method 4 apple
@Beta 的回答 包含了关键指针:在 GNU make
中,被标记为 export
的变量仅对 [作为规则一部分的] 配方命令(命令)可用,遗憾的是 不能用于 $(shell ...)
的调用(它们只能看到 make
启动时所看到的环境)。
但是有一个解决方法:将导出的变量显式作为环境变量传递给 shell
函数:
update := $(shell somevar='$(somevar)' perl -e 'print "$$ENV{somevar}"')
在shell命令之前加上<var>=<val>
,这将把该定义作为环境变量添加到命令所看到的环境中-这是一个通用的shell功能。
注意:@Kaz在评论中指出,如果$(somevar)
包含某些字符,则此方法会出现问题,因为变量扩展是文字直接复制(没有转义),这可能会破坏生成的shell命令,并建议使用以下变体来处理嵌入式'
实例(将输入值拆分为带有引号'
的单引号子字符串):
update := $(shell somevar='$(subst ','\'',$(somevar))' perl -e 'print "$$ENV{somevar}"')
除了多行值(这种情况比较少见,目前没有解决方法),此方法适用于所有值。
另外需要注意的是,在值中出现的文字 $
必须表示为 $$
,否则 make
会将其视为引用其自身变量。
请注意,我故意没有选择 OP 的原始语句 update := $(shell echo "v=$$somevar")
进行演示,因为它包含一个混淆问题:由于 shell 如何评估命令行,somevar=apple echo v=$somevar
不会计算为 v=apple
,因为 $somevar
引用在 somevar=apple
生效之前被扩展。在这种情况下实现所需的效果,您必须使用两个语句:update := $(shell export somevar="$(somevar)"; echo "v=$$somevar")
至于错误与功能之争:
虽然可以认为 shell
函数应该看到与配方命令相同的环境,但文档没有做出这样的承诺 - 请参阅 http://www.gnu.org/software/make/manual/make.html#Shell-Function。相反,http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion 仅提到将导出的变量提供给配方命令。
$(PATH)
),那么手头的方法仍然可能是一个选择。 - mklement0$(subst ...)
将 '
的出现替换为 '\''
。然后,在 shell 端在单引号之间插入结果数据。 (我今天就是这样解决的。) - Kaz'
字符,但无法解决文字 $
字符的问题:make 会不可避免地将其自己扩展(作为对其自身变量的引用),在应用 $(subst …)
之前 - 请参见我的更新。 - mklement0FOO := $(BAR)
,然后在某个地方将 $(FOO)
插入到 shell 脚本中,我希望 $(BAR)
被扩展。这个扩展是在分配 FOO
时完成的,因为我使用了一个立即变量 (:=
)。如果 $(BAR)
产生一个美元符号,那么这个美元符号现在只是 FOO
中的数据,不会再引起任何问题。如果我们在 shell 脚本中用单引号括起来得到 $(FOO)
,那么这个美元符号也不会引起问题。在我们考虑与 shell 通信之前,I am $HOME
已经出现了问题。 - Kaz运行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在运行命令时处理扩展(你仍然需要导出变量)
$(shell ...)
只能看到在 make
启动时已经存在于 make
自己环境中的变量;(b) 在 make
命令行上定义变量会覆盖 makefile 中同名的定义。 - mklement0export
不能与$(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
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 ...)
,您仍需要进行 shell_escape
。也就是说,请再看一个 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
$(subst ..)
将单引号转义为'\''
来解决了这个问题。然后将结果在单引号之间插入到shell脚本中。 - Kaz