【发布时间】:2011-02-19 18:57:34
【问题描述】:
somevar := apple
export somevar
update := $(shell echo "v=$$somevar")
all:
@echo $(update)
我希望将 apple 作为命令的输出,但它是空的,这让我认为 export 和 := 变量扩展发生在不同的阶段。如何克服?
【问题讨论】:
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"'
【讨论】:
$(subst ..) 将单引号转义为'\'' 解决了这个问题。然后将结果插入到单引号之间的 shell 脚本中。
@Beta's answer 包含关键指针:对于 GNU make,标有 export 的变量仅适用于 [为这些 shell 启动的]recipe 命令(作为规则一部分的命令),遗憾的是不适用于$(shell ...)的调用(它们只看到make在启动时看到的环境)。
有一个解决方法,但是:明确将导出的变量作为环境变量传递给shell函数:
update := $(shell somevar='$(somevar)' perl -e 'print "$$ENV{somevar}"')
通过在 shell 命令前添加 <var>=<val>,该定义将作为环境变量添加到命令看到的环境中 - 这是一个通用的 shell 功能。
警告:@Kaz 在评论中指出,如果$(somevar) 包含某些字符,则此方法会出现错误,因为变量扩展是逐字逐句(没有转义),这可能会破坏生成的 shell 命令,并建议以下 变体也可以与嵌入的 ' 实例一起使用(将输入值分解为单引号子字符串,并在其中拼接了引号 '): p>
update := $(shell somevar='$(subst ','\'',$(somevar))' perl -e 'print "$$ENV{somevar}"')
这应该适用于除 multi-line 以外的所有值(这种情况很少见;据我所知,没有针对多行值的解决方法)。
附带说明,文字 $ 字符。 in 值必须表示为$$,否则make 会将它们解释为对其自身变量的引用。
请注意,我故意不选择 OP 的 original 语句 update := $(shell echo "v=$$somevar") 进行演示,因为它包含一个混淆问题的陷阱:由于 shell 如何评估命令行,somevar=apple echo v=$somevar不会评估为v=apple,因为$somevar 引用在somevar=apple 生效之前扩展。为了在这种情况下达到预期的效果,您必须使用 2 语句:update := $(shell export somevar="$(somevar)"; echo "v=$$somevar")
关于 bug 与功能的争论:
虽然可以说shell 函数应该看到与配方命令相同的环境,但文档没有做出这样的承诺 - 请参阅 http://www.gnu.org/software/make/manual/make.html#Shell-Function。相反,http://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion 只提到使导出的变量可用于 recipe 命令。
【讨论】:
$(PATH)),手头的方法可能仍然是一种选择。
$(subst ...) 将出现的' 替换为'\''。然后,在 shell 端的单引号之间插入结果数据。 (我今天为此苦苦挣扎并以这种方式解决了。)
' 字符。但不能解决文字 $ 字符的问题。:make 总是会自行扩展它们(如引用它自己的变量),之前 $(subst …) 被应用 - 请参阅我的更新。
FOO := $(BAR),然后在某个地方将$(FOO) 插入到shell 脚本中,我希望扩展$(BAR)。该扩展在分配FOO 时完成,因为我使用了立即变量(:=)。如果$(BAR) 产生一个美元符号,那么该美元符号现在只是FOO 中的数据,不会引起更多问题。如果我们在 shell 脚本中的单引号之间得到$(FOO),那么那个美元符号也不会在那里引起问题。 I am $HOME 在我们考虑与 shell 通信之前就已经损坏了。
运行生成文件
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 自己的环境中已经存在的变量; (b) 在make 的命令行上定义变量会覆盖makefile 中同名的定义。
虽然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),所以无论如何你都会shell_escape,即使GNU Make 将导出的变量传递给$(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
【讨论】: