126

我正在尝试使用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 $$FILES,echo ${FILES}echo $(FILES),但没有运气。

4

2 回答 2

197

和:

FILES = $(shell ls)

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

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

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

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

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

all:
        FILES="$(shell ls)"

但是,每一行都由一个单独的 shell 运行,因此该变量将无法保存到下一行,因此您必须立即使用它:

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

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

        echo *

作为你的shell命令。

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


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

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

于 2012-04-05T07:39:00.367 回答
63

此外,除了 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 命令。这会使你的 make 工作运行得更慢,甚至会产生一些令人惊讶的后果。

于 2012-04-09T23:22:03.363 回答