bash 如何在 Makefile 中使用 shell 命令

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10024279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:55:00  来源:igfitidea点击:

How to use shell commands in Makefile

bashmakefileecho

提问by Adam Matan

I'm trying to use the result of lsin other commands (e.g. echo, rsync):

我正在尝试使用ls其他命令(例如 echo、rsync)中的结果:

all:
    <Building, creating some .tgz files - removed for clarity>
    FILES = $(shell ls)
    echo $(FILES)

But I get:

但我得到:

make
FILES = Makefile file1.tgz file2.tgz file3.tgz
make: FILES: No such file or directory
make: *** [all] Error 1

I've tried using echo $$FILES, echo ${FILES}and echo $(FILES), with no luck.

我试过使用echo $$FILES,echo ${FILES}echo $(FILES),但没有运气。

回答by torek

With:

和:

FILES = $(shell ls)

indented underneath alllike that, it's a build command. So this expands $(shell ls), then tries to run the command FILES ....

all像这样在下面缩进,这是一个构建命令。所以这会展开$(shell ls),然后尝试运行命令FILES ...

If FILESis supposed to be a makevariable, these variables need to be assigned outside the recipe portion, e.g.:

如果FILES应该是make变量,则需要在配方部分之外分配这些变量,例如:

FILES = $(shell ls)
all:
        echo $(FILES)

Of course, that means that FILESwill be set to "output from ls" beforerunning any of the commands that create the .tgz files. (Though as Kaz notesthe variable is re-expanded each time, so eventually it will include the .tgz files; some make variants have FILES := ...to avoid this, for efficiency and/or correctness.1)

当然,这意味着运行任何创建 .tgz 文件的命令之前FILES它将被设置为“输出ls” 。(尽管Kaz 指出变量每次都会重新扩展,因此最终它将包含 .tgz 文件;为了效率和/或正确性,一些 make 变体必须避免这种情况。1FILES := ...

If FILESis supposed to be a shell variable, you can set it but you need to do it in shell-ese, with no spaces, and quoted:

如果FILES应该是一个shell变量,你可以设置它,但你需要在shell-ese中进行,没有空格,并引用:

all:
        FILES="$(shell ls)"

However, each line is run by a separate shell, so this variable will not survive to the next line, so you must then use it immediately:

然而,每一行都由一个单独的 shell 运行,所以这个变量不会保留到下一行,所以你必须立即使用它:

        FILES="$(shell ls)"; echo $$FILES

This is all a bit silly since the shell will expand *(and other shell glob expressions) for you in the first place, so you can just:

这有点傻,因为 shell 首先会*为您扩展(和其他 shell glob 表达式),所以您可以:

        echo *

as your shell command.

作为你的 shell 命令。

Finally, as a general rule (not really applicable to this example): as esperantonotes in comments, using the output from lsis not completely reliable (some details depend on file names and sometimes even the version of ls; some versions of lsattempt to sanitize output in some cases). Thus, as l0b0and idelicnote, if you're using GNU make you can use $(wildcard)and $(subst ...)to accomplish everything inside makeitself (avoiding any "weird characters in file name" issues). (In shscripts, including the recipe portion of makefiles, another method is to use find ... -print0 | xargs -0to avoid tripping over blanks, newlines, control characters, and so on.)

最后,作为一般规则(并非真正适用于本示例):正如世界语注释中的注释,使用 from 的输出ls并不完全可靠(某些细节取决于文件名,有时甚至取决于 的版本ls;某些版本的ls尝试清理输出在某些情况下)。因此,正如l0b0理想的注释,如果您使用 GNU make,您可以使用$(wildcard)$(subst ...)完成其内部的所有内容make(避免任何“文件名中的奇怪字符”问题)。(在sh脚本中,包括 makefile 的配方部分,另一种方法是find ... -print0 | xargs -0用来避免被空格、换行符、控制字符等绊倒。)



1The GNU Make documentation notes further that POSIX make added ::=assignment in 2012. I have not found a quick reference link to a POSIX document for this, nor do I know off-hand which makevariants support ::=assignment, although GNU make does today, with the same meaning as :=, i.e., do the assignment right now with expansion.

1 GNU Make 文档进一步指出 POSIX::=在 2012 年增加了分配。我还没有找到一个指向 POSIX 文档的快速参考链接,我也不知道哪些make变体支持::=赋值,尽管 GNU make 今天做了,与:=,即,现在用扩展做赋值。

Note that VAR := $(shell command args...)can also be spelled VAR != command args...in several makevariants, including all modern GNU and BSD variants as far as I know. These other variants do not have $(shell)so using VAR != command args...is superior in both being shorter andworking in more variants.

请注意,VAR := $(shell command args...)也可以拼写VAR != command args...为多种make变体,包括据我所知的所有现代 GNU 和 BSD 变体。这些其他变体没有,$(shell)因此在更VAR != command args...在更多变体中工作方面都具有优势。

回答by Kaz

Also, in addition to torek's answer: one thing that stands out is that you're using a lazily-evaluated macro assignment.

此外,除了 torek 的回答:突出的一件事是您正在使用延迟评估的宏分配。

If you're on GNU Make, use the :=assignment instead of =. This assignment causes the right hand side to be expanded immediately, and stored in the left hand variable.

如果您使用 GNU Make,请使用:=赋值而不是=. 此赋值导致右侧立即展开,并存储在左侧变量中。

FILES := $(shell ...)  # expand now; FILES is now the result of $(shell ...)

FILES = $(shell ...)   # expand later: FILES holds the syntax $(shell ...)

If you use the =assignment, it means that every single occurrence of $(FILES)will be expanding the $(shell ...)syntax and thus invoking the shell command. This will make your make job run slower, or even have some surprising consequences.

如果您使用=赋值,则意味着每次出现的$(FILES)都将扩展$(shell ...)语法并因此调用 shell 命令。这将使您的 make 作业运行得更慢,甚至会产生一些令人惊讶的后果。