如何将命令的输出分配给Makefile变量


224

仅当安装的Python大于某个版本(例如2.5)时,我才需要有条件地执行一些make规则。

我以为可以执行以下操作:

python -c 'import sys; print int(sys.version_info >= (2,5))'

然后在ifeqmake语句中使用输出(如果可以,则为“ 1”,否则为“ 0”)。

在一个简单的bash shell脚本中,它只是:

MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`

但这在Makefile中不起作用。

有什么建议?我可以使用任何其他明智的解决方法来实现此目的。


在我的Makefile中为我执行其他脚本的命令工作周围出现了奇怪的勾号。可能是别的东西。
Leif Gruenwoldt 2014年

Answers:


345

shell像这样使用Make 内置MY_VAR=$(shell echo whatever)

me@Zack:~$make
MY_VAR IS whatever

me@Zack:~$ cat Makefile 
MY_VAR := $(shell echo whatever)

all:
    @echo MY_VAR IS $(MY_VAR)

34
shell不是标准的Make内置命令。这是内置的GNU Make。
Dereckson

12
stackoverflow.com/a/2373111/12916添加了有关转义的重要说明$
杰西·格里克

6
这个简单的例子有效。它也可用于shell命令管道。但是至关重要的是,您应该在外壳程序命令中使用$$来表示$
Sergey P.又名蔚蓝

28
虽然问题有点老了,但最好执行MY_VAR:= $(shell ...),否则每次评估MY_VAR时,它将再次执行$(shell ...)。
Russ Schultz

我在外壳和

29

将作业包装在中eval对我来说很有效。

# dependency on .PHONY prevents Make from 
# thinking there's `nothing to be done`
set_opts: .PHONY
  $(eval DOCKER_OPTS = -v $(shell mktemp -d -p /scratch):/output)

6
“注意:这里的@true阻止了Make认为没有任何事情要做。” 嗯,这就是.PHONY always make these targets目的。
underscore_d

谢谢!这可以帮助我绕过makefile中的“怪异bash”
Nam G VU

19

这是在配方中包含管道和变量分配的更复杂的示例:

getpodname:
    # Getting pod name
    @eval $$(minikube docker-env) ;\
    $(eval PODNAME=$(shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
    echo $(PODNAME)

2
万一方便,我可以使用类似的方法来获取PODNAME部署名称:$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='{.items[0].metadata.name}'"))
Jan Richter

语法使我困惑了一秒钟,直到我意识到您同时使用了shell内置的 eval(在docker-env行上)和make函数 eval(在下一行上)。
Brian Gordon

17

我正在写一个答案,以增加对解决问题的实际语法的可见性。不幸的是,对于那些寻求合理问题的简单答案的人来说,琐碎的事情可能会变得非常头痛。

将以下内容放入文件“ Makefile”。

MY_VAR := $(shell python -c 'import sys; print int(sys.version_info >= (2,5))')

all:
    @echo MY_VAR IS $(MY_VAR)

您希望看到的行为如下(假设您已安装最新的python)。

make
MY_VAR IS 1

如果将以上文本复制并粘贴到Makefile中,您会得到吗?可能不是。您可能会收到类似此处报告的错误:

makefile:4:***缺少分隔符。停止

原因:因为尽管我个人使用的是真正的标签,但是Stack Overflow(试图提供帮助)会将我的标签转换为许多空格。您,沮丧的互联网公民,现在复制此内容,以为您现在使用的文字与我相同。现在,make命令读取空格并发现“ all”命令的格式不正确。因此,复制上面的文本,将其粘贴,然后将“ @echo”之前的空格转换为选项卡,最后,该示例有望为您工作。


取决于要粘贴到的编辑器。我只是复制并粘贴到Eclipse Makefile编辑器中,并得到一个前导选项卡(根据需要)。
Technophile

哦,我没有想到这一点。原子在这里。
AlanSE

11

当心这样的食谱

target:
    MY_ID=$(GENERATE_ID);
    echo $MY_ID;

它做错了两件事。配方中的第一行与第二行在单独的shell实例中执行。同时,该变量丢失。第二件事是错误的,$是没有逃脱。

target:
    MY_ID=$(GENERATE_ID); \
    echo $$MY_ID;

这两个问题都已修复,并且该变量可用。反斜杠将两行结合在一起以在单个外壳中运行,因此可以进行变量的设置和变量后缀的读取。

我意识到原来的帖子说过如何将shell命令的结果转换为MAKE变量,并且此答案显示了如何将其转换为shell变量。但是其他读者可能会受益。

最后一项改进是,如果消费者希望设置“环境变量”,则必须将其导出。

my_shell_script
    echo $MY_ID

在makefile中需要这个

target:
    export MY_ID=$(GENERATE_ID); \
    ./my_shell_script;

希望能对某人有所帮助。通常,应该避免在食谱之外进行任何实际工作,因为如果有人使用带有“ --dry-run”选项的makefile,仅查看它会做什么,就不会有任何不良影响。每次$(shell)调用都会在编译时进行评估,并且可能会意外地执行某些实际工作。最好在可能的情况下,将实际工作(如生成ID)留在配方内部。


9

使用GNU Make,您可以使用shelleval存储,运行和分配任意命令行调用的输出。下面的示例与使用的示例之间的区别:=:=分配(一旦遇到)仅发生一次。设置的递归扩展变量=更加“懒惰”。保留对其他变量的引用,直到引用变量本身为止,并且每次引用该变量时都会进行后续递归扩展,这对于制作“一致,可调用的代码段”是理想的。有关更多信息,请参见有关设置变量的手册

# Generate a random number.
# This is not run initially.
GENERATE_ID = $(shell od -vAn -N2 -tu2 < /dev/urandom)

# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))

# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
    @echo $(MY_ID)
# This recursively expands SET_ID, which calls the shell command and sets MY_ID
    $(SET_ID)
# This will now be a random number
    @echo $(MY_ID)
# Recursively expand SET_ID again, which calls the shell command (again) and sets MY_ID (again)
    $(SET_ID)
# This will now be a different random number
    @echo $(MY_ID)

你有我遇到的第一个很好的解释。谢谢。虽然有一点要补充的是,如果$(SET_ID)在里面生活的if条款那是假的,然后它仍称
罗马

它之所以被调用是因为if stmts是在makefile编译时而不是在运行时求值的。有关运行时的特定说明,请将其放入配方中。如果它们是有条件的,请添加以bash / shell编写的stmts作为配方的一部分。
朱拉杰

0

在下面的示例中,我将Makefile文件夹路径存储到目标中LOCAL_PKG_DIR,然后在其中使用LOCAL_PKG_DIR变量。

生成文件:

LOCAL_PKG_DIR := $(shell eval pwd)

.PHONY: print
print:
    @echo $(LOCAL_PKG_DIR)

终端输出:

$ make print
/home/amrit/folder
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.