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

Posted

技术标签:

【中文标题】如何将命令的输出分配给 Makefile 变量【英文标题】:How to assign the output of a command to a Makefile variable 【发布时间】:2011-01-02 11:05:52 【问题描述】:

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

我想我可以做一些类似执行的事情:

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

然后在ifeq make 语句中使用输出(如果正常则为'1',否则为'0')。

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

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

但这在 Makefile 中不起作用。

有什么建议吗?我可以使用任何其他合理的解决方法来实现这一点。

【问题讨论】:

在 Makefile 中为我执行其他脚本的命令工作周围的奇怪反勾号。可能是别的东西。 【参考方案1】:

使用 Make shellMY_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)

【讨论】:

shell 不是标准的 Make 内置命令。这是一个 GNU Make 内置。 ***.com/a/2373111/12916 添加了关于转义$ 的重要说明。 这个简单的例子有效。它也适用于 shell 命令管道。但必须在 shell 命令中使用 $$ 来表示 $ 虽然问题有点老了,但最好执行 MY_VAR := $(shell ...),否则每次评估 MY_VAR 时,它都会再次执行 $(shell ...)。 shell echo whatever 替换为python -c 'import sys; print int(sys.version_info >= (2,5))'。您会收到“意外标记附近的语法错误”。我不明白有人怎么认为这回答了这个问题。谁能解释一下我错过了什么?【参考方案2】:

小心这样的食谱

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

它做错了两件事。配方中的第一行在与第二行不同的 shell 实例中执行。变量同时丢失。第二个错误是$ 没有被转义。

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

这两个问题都已修复,变量可用。反斜杠将两行结合起来在一个 shell 中运行,因此变量的设置和变量后缀的读取是有效的。

我意识到原始帖子说如何将 shell 命令的结果放入 MAKE 变量中,而这个答案显示了如何将其放入 shell 变量中。但其他读者可能会受益。

最后一个改进,如果消费者期望设置一个“环境变量”,那么你必须导出它。

my_shell_script
    echo $MY_ID

在 makefile 中需要这个

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

希望对某人有所帮助。一般来说,应该避免在食谱之外做任何实际工作,因为如果有人使用带有“--dry-run”选项的makefile,只看它会做什么,它不会有任何不良副作用。每个$(shell) 调用都在编译时进行评估,一些实际工作可能会意外完成。如果可能,最好将真正的工作(例如生成 id)留在食谱内部。

【讨论】:

【参考方案3】:

将作业包装在 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)

【讨论】:

"注意:这里的@true 可以防止 Make 认为没有什么可以做。" 嗯,这就是 .PHONY always make these targets 的用途。 谢谢!这有助于我绕过 makefile 中的“奇怪的 bash”【参考方案4】:

这是一个更复杂的示例,其中包含配方中的管道和变量赋值:

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)

【讨论】:

如果它派上用场,我会使用类似的方法通过部署名称获取PODNAME$(eval PODNAME=$(shell sh -c "kubectl get pod -l app=sqlproxy -o jsonpath='.items[0].metadata.name'")) 语法让我困惑了一秒钟,直到我意识到你同时使用了 shell builtin eval(在 docker-env 行上)和 make 函数 i> eval(在下一行)。 你能解释一下为什么需要这种语法吗?【参考方案5】:

使用 GNU Make,您可以使用 shelleval 来存储、运行和分配任意命令行调用的输出。下面的示例与使用:= 的示例之间的区别在于:= 分配只发生一次(遇到时)并且一劳永逸。使用= 设置的递归扩展变量有点“懒惰”;对其他变量的引用一直保持到变量本身被引用,并且每次引用变量时都会发生后续的递归扩展,这对于实现“一致、可调用、sn-ps”是可取的。请参阅the manual on setting variables 了解更多信息。

# 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 编译时而不是在运行时评估的。对于运行时特定的指令,将它们放在食谱中。如果它们是有条件的,则添加 if stmts,用 bash / shell 编写,作为配方的一部分。 +100 进行简单、中肯的解释【参考方案6】:

我正在写一个答案,以提高对解决问题的实际语法的可见性。不幸的是,对于寻求合理问题的简单答案的人来说,可能被认为微不足道的事情可能会成为一个非常头疼的问题。

将以下内容放入文件“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: *** missing separator. Stop

为什么:因为虽然我个人使用的是真正的制表符,但 Stack Overflow(试图提供帮助)会将我的制表符转换为多个空格。您,沮丧的互联网公民,现在复制此内容,并认为您现在拥有与我使用的相同的文本。 make 命令现在读取空格并发现“all”命令的格式不正确。所以复制上面的文本,粘贴它,然后将“@echo”之前的空格转换为制表符,这个例子最终应该对你有用。

【讨论】:

取决于您要粘贴到的编辑器。我刚刚复制并粘贴到 Eclipse Makefile 编辑器中,并获得了一个前导选项卡(根据需要)。 哦,我没想到。原子在这里。 那是因为 Eclipse 通过识别 makefile 语法将这些空格转换为制表符。在 SO 网页中,肯定是空格。【参考方案7】:

来自制作手册

shell 赋值运算符 ‘!=’ 可用于执行 shell 脚本并将 > 变量设置为其输出。此运算符首先计算右侧,然后将 > 该结果传递给 shell 以执行。如果执行结果以 >newline 结尾,则删除该换行符;所有其他换行符都被空格替换。然后将 >resulting 字符串放入命名的递归扩展变量中。 >例如:

hash != printf '\043'

file_list != find . -name '*.c'

source

【讨论】:

【参考方案8】:

在下面的示例中,我将 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

【讨论】:

以上是关于如何将命令的输出分配给 Makefile 变量的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Bash 命令的输出分配给变量? [复制]

如何在使用“ls”命令以及将输出分配给变量时抑制标准错误?

如何执行 shell 命令,并将输出分配给 configure.ac 中的 AC_DEFINE 变量

我正在使用来自 python 的 powershell 命令获取一些数据,而 Windows 中没有 cmd!如何将此输出分配给字符串变量?

将shell命令的输出分配给变量[重复]

将 os.system 的输出分配给一个变量并防止它显示在屏幕上[重复]