在 makefile 中调用 bash 命令 [英] Invoking bash commands in makefile
问题描述
在 makefile
内,我试图检查fileA是否比fileB更新得最近.使用之前的一些帖子(此和
Inside of a makefile
, I'm trying to check if fileA was modified more recently than fileB. Using a few prior posts (this and this) as references, I've come up with this as an attempt to store the time since last file modification as a variable:
(我希望这是在 make
配方之外的函数中,但一次只能执行一个步骤.)
(I'd rather this be in a function outside of a make
recipe, but one step at a time.)
.PHONY: all clean
all : (stuff happens here)
radio :
BASE_MOD_TIME="$( expr $(date +%s) - $(date +%s -r src/radio_interface/profile_init.c) )"
@echo "$(BASE_MOD_TIME)"
我认为我应该将 expr
命令的输出分配给变量 BASE_MOD_TIME
,但是输出是:
I thought that I would be assigning the output of the expr
command to a variable, BASE_MOD_TIME
, but the output is:
bash-4.1$
BASE_MOD_TIME=""
echo ""
我在这里做错了什么?保存 ls -l </code>输出的简单尝试也没有这样的效果.
What am I doing wrong here? Simple attempts to save the output of ls -l
also didn't work like this.
推荐答案
使变量通常是全局变量,并且通常不会在配方中设置make变量.配方只是外壳程序要执行的命令的列表,因此在配方中看起来像变量分配的就是外壳程序变量分配.
Make variables are normally global, and you don't normally set make variables in a recipe. A recipe is simply a list of commands to be executed by a shell, and so what looks like a variable assignment in a recipe is a shell variable assignment.
但是, make
配方中的每一行都在其自己的shell子进程中运行.因此,一行中设置的变量在另一行中不可见;他们不是持久的.这使得在配方中设置外壳变量的用处不大.[注1]
However, each line in a make
recipe is run in its own shell subprocess. So a variable set in one line won't be visible in another line; they are not persistent. That makes setting shell variables in recipes less useful. [Note 1]
但是您可以在行的末尾使用反斜杠转义,将配方的多行组合到单个shell命令中,并记住用分号终止各个命令(或者更好的是,将它们与&&
),因为不会将反斜杠转义的换行符传递给shell.另外,不要忘记转义 $
字符,这样它们将被传递到外壳,而不是被 make
解释.
But you can combine multiple lines of a recipe into a single shell command using the backslash escape at the end of the line, and remembering to terminate the individual commands with semicolons (or, better, link them with &&
), because the backslash-escaped newline will not be passed to the shell. Also, don't forget to escape the $
characters so they will be passed to the shell, rather than being interpreted by make
.
因此您可以执行以下操作:
So you could do the following:
radio:
@BASE_MOD_TIME="$$( expr $$(date +%s) - $$(date +%s -r src/radio_interface/profile_init.c) )"; \
echo "$$BASE_MOD_TIME"; \
# More commands in the same subprocess
但是,如果有多个命令,那将变得很尴尬,更好的解决方案通常是编写一个Shell脚本并从配方中调用它(尽管这意味着Makefile不再是独立的.)
But that gets quite awkward if there are more than a couple of commands, and a better solution is usually to write a shell script and invoke it from the recipe (although that means that the Makefile is no longer self-contained.)
Gnu make提供了两种在配方中设置make变量的方法:
Gnu make provides two ways to set make variables in a recipe:
您可以通过添加类似于以下内容的行来创建特定于目标的变量(并非完全位于目标本地):
You can create a target-specific variable (which is not exactly local to the target) by adding a line like:
target: var := value
要通过shell命令设置变量,请使用 shell
函数:
To set the variable from a shell command, use the shell
function:
target: var := $(shell ....)
此变量将在目标配方中以及由目标触发的所有依赖项中可用.请注意,一个依赖项仅被评估一次,因此,如果它可以由另一个目标触发,则依赖于 make
解析的顺序,特定于目标的变量在该依赖项中可能可用或可能不可用.依赖性.
This variable will be available in the target recipe and all dependencies triggered by the target. Note that a dependency is only evaluated once, so if it could be triggered by a different target, the target-specific variable might or might not be available in the dependency, depending on the order in which make
resolves dependencies.
由于总是延迟配方的扩展,因此可以在配方内使用 eval
函数来延迟make变量的分配. eval
函数可以很好地放置在配方中的任何位置,因为它的值是空字符串.但是, eval
进行变量分配会使变量分配成为全局变量.它在整个Makefile中都是可见的,但在其他配方中的值将再次取决于make评估配方的顺序,而该顺序不一定是可预测的.
Since the expansion of recipes is always deferred, you can use the eval
function inside a recipe to defer the assignment of a make variable. The eval
function can be placed pretty well anywhere in a recipe because its value is the empty string. However, eval
ing a variable assignment makes the variable assignment global; it will be visible throughout the makefile, but its value in other recipes will depend, again, on the order in which make evaluates recipes, which is not necessarily predictable.
例如:
radio:
$(eval var = $(shell ...))
注意:
- 您可以使用
.ONESHELL:
伪目标更改此行为,但这将应用于整个Makefile;无法将单个配方标记为在单个子流程中执行.由于更改行为可能会以意想不到的方式破坏食谱,因此我通常不建议使用此功能.
- You can change this behaviour using the
.ONESHELL:
pseudo-target, but that will apply to the entire Makefile; there is no way to mark a single recipe as being executed in a single subprocess. Since changing the behaviour can break recipes in unexpected ways, I don't usually recommend this feature.
这篇关于在 makefile 中调用 bash 命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!