如何在Makefile中使用shell命令

时间:2012-04-05 07:25:54

标签: macos bash makefile echo

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

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

但我明白了:

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

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

2 个答案:

答案 0 :(得分:113)

使用:

FILES = $(shell ls)

all下面缩进,这是一个构建命令。因此,这会扩展$(shell ls),然后尝试运行命令FILES ...

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

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

当然,这意味着在运行创建.tgz文件的任何命令之前,FILES将设置为“ls的输出。 (虽然每次重新扩展变量为Kaz notes,但最终它将包含.tgz文件;为了效率和/或正确性,一些变体使用FILES := ...来避免这种情况。 1

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

all:
        FILES="$(shell ls)"

但是,每一行都由一个单独的shell运行,因此这个变量不会存活到下一行,所以你必须立即使用它:

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

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

        echo *

作为你的shell命令。

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


1 The GNU Make documentation notes further that POSIX make added ::= assignment in 2012。我没有为此找到POSIX文档的快速参考链接,也不知道哪些make变体支持::=赋值,尽管GNU make今天做了,其含义与{{{ 1}},即现在通过扩展进行分配。

请注意:=也可以在几个VAR := $(shell command args...)变体中拼写VAR != command args...,据我所知,包括所有现代GNU和BSD变体。这些其他变体没有make因此使用$(shell)在更短的更多变种中都是优越的。

答案 1 :(得分:39)

此外,除了torek的答案之外:有一点很突出,那就是你正在使用懒惰评估的宏分配。

如果您使用的是GNU Make,请使用:=分配而不是=。此分配使右侧立即展开,并存储在左侧变量中。

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

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

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