如何在 Makefile 使用 shell 命令

我尝试在其他命令中使用 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),但没有成功。

285204 次浏览

配合:

FILES = $(shell ls)

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

如果 FILES应该是一个 make变量,那么这些变量需要在配方部分之外赋值,例如:

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

当然,这意味着 FILES将被设置为“ output from ls之前,它运行创建。TGZ 档案。(尽管作为 凯兹的笔记变量每次都会重新展开,因此最终它将包含。为了提高效率和/或正确性,一些变体使用 FILES := ...来避免这种情况

如果 FILES应该是一个 shell 变量,那么您可以设置它,但是您需要使用 shell 语言来设置它,不要使用空格,并引用:

all:
FILES="$(shell ls)"

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

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

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

        echo *

作为您的 shell 命令。

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


1 GNUMake 文档进一步指出,POSIX 在2012年添加了 ::=任务.我还没有找到一个到 POSIX 文档的快速参考链接,我也不知道哪个 make变体支持 ::=分配,尽管 GNU make 今天做到了,与 :=具有相同的意义,也就是说,现在就通过展开来完成分配。

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

另外,除了 Torek 的回答之外,还有一点值得注意的是,您正在使用一个延迟计算的宏赋值。

如果您使用 GNUMake,请使用 :=分配而不是 =分配。这种赋值会立即展开右侧,并将其存储在左侧变量中。

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


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

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