Variables Makefile变量初始化和导出
我希望苹果作为命令的输出,但它是空的,这让我想到导出和Variables Makefile变量初始化和导出,variables,makefile,export,Variables,Makefile,Export,我希望苹果作为命令的输出,但它是空的,这让我想到导出和:=变量扩展在不同的阶段进行。如何克服这个问题?运行makefile somevar := apple export somevar update := $(shell echo "v=$$somevar") all: @echo $(update) 为我提供(环境中未定义foo) 尝试使用带引号的表单(update:=“v=$$somevar”),并在命令运行时让shell处理扩展(您仍然需要导出)问题是export将变量导出到
:=
变量扩展在不同的阶段进行。如何克服这个问题?运行makefile
somevar := apple
export somevar
update := $(shell echo "v=$$somevar")
all:
@echo $(update)
为我提供(环境中未定义foo)
尝试使用带引号的表单(
update:=“v=$$somevar”
),并在命令运行时让shell处理扩展(您仍然需要导出)问题是export
将变量导出到命令使用的子shell;它在其他作业中不可扩展。因此,不要试图从规则之外的环境中获取它
$ make
>
>apple
$ make foo=bar
>
>apple
$ export foo=bar; make
>bar
>apple
$ export foo=bar; make foo=bar
>bar
>bar
包含关键指针:对于GNUmake
,标有export
的变量仅可用于[为]配方命令启动的shell(作为规则一部分的命令),遗憾的是不可用于调用$(shell…
(他们只看到make
启动时看到的环境)
有一个解决方法,但是:将导出的变量作为环境变量显式地传递给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"'
通过在shell命令前面加上=
,该定义将作为环境变量添加到命令所看到的环境中—这是一个通用的shell功能
警告:@Kaz在一篇评论中指出,如果$(somevar)
包含某些字符,则此方法会出现错误,因为变量扩展是逐字(无转义),这可能会破坏生成的shell命令,并建议以下变体也可用于嵌入式”
实例(将输入值拆分为单引号的子字符串,并在其中拼接引号”
):
这应该适用于除多行值以外的所有值(这很少见;我知道没有多行值的解决方法)
另一方面,literal$
chars.in值必须表示为$
,否则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
函数应该看到与配方命令相同的环境,但文档没有做出这样的承诺-请参阅。相反,只提到让配方命令可以使用导出的变量。尽管export
不能很好地使用$(shell…)
,有一个简单的解决方法。我们可以通过命令行将数据传递给shell脚本
当然,environment Passation对转义和引用的问题很健壮。但是,shell语言有一个单引号引用方法“…”
,它可以处理所有事情。唯一的问题是,无法在其中获取单引号;但当然,这可以通过终止引号、反斜杠转义nee来解决删除单个报价并开始新报价:换句话说:
update := $(shell somevar='$(subst ','\'',$(somevar))' perl -e 'print "$$ENV{somevar}"')
在由$(shell…
执行的shell脚本中,我们只生成一个形式为var='$(…)'
的变量赋值,其中$(…)
是一些make表达式,它插入适当的转义材料。因此,Makefile
:
ab'cd -> 'ab'\''cd'
$ make
cat file.txt
apple with 'quoted' "stuff" and dollar $signs
export somevar
update := $(shell env > file.txt)
样本运行:
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
如果要将环境变量与命令通信,可以使用shell语法VAR0=val0 VAR1=val1…VARn=valn command arg…
。这可以通过对上面的Makefile
进行一些小改动来说明:
ab'cd -> 'ab'\''cd'
$ make
cat file.txt
apple with 'quoted' "stuff" and dollar $signs
export somevar
update := $(shell env > file.txt)
运行:
file.txt
包含一个环境变量转储,在这里我们可以看到somevar
。如果GNU Make中的export
做了正确的事情,我们就可以做到:
$ make
grep somevar file.txt
somevar=apple with 'quoted' "stuff" and dollar $signs
但最终结果是一样的
由于您想要的最终结果是echo$(update)
,因此您无论如何都会shell\u转义
,即使GNU Make将导出的变量传递到$(shell…
)
ab'cd -> 'ab'\''cd'
$ make
cat file.txt
apple with 'quoted' "stuff" and dollar $signs
export somevar
update := $(shell env > file.txt)
输出:
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)
我需要保持shell函数不在target命令中的方式,因为它在现实生活中有条件语句。+1用于显示变量;短的是:(a)
$(shell…
只看到make
启动时自己的环境中已经存在的变量;(b)在make
的命令行上定义变量会覆盖makefile中相同名称的定义。使用第一个表达式“apple”的输出如何,并且在makefile之外不做任何事情?事实上这是一个bug[这不是固定的,也许永远也不会。谢谢,我会尽量不用奇怪的makefile问题来打扰你。@Michael:从我对GNUMake手册的阅读来看,这不是一个bug。(而且你不必在我回答的评论前面加上@Beta。)这并不奇怪。我需要这个。为什么我需要这个?因为环境是将任意字符串传递给外部脚本的唯一防弹方法。例如,假设我有一个变量V,它包含以下内容:各种类型的引号、重要的shell元字符(如美元)、反斜杠转义符等等。我想要一个shell脚本来se变量$V.@Kaz:听起来很危险,但可行。上面的方法之一是吗