如何在Makefile中使用shell命令

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

 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) ,没有运气。

附:

 FILES = $(shell ls) 

all这些下面缩进,这是一个构build命令。 所以这扩展了$(shell ls) ,然后尝试运行命令FILES ...

如果FILES应该是一个makevariables,则这些variables需要在配方部分之外进行分配,例如:

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

当然,这意味着运行任何创build.tgz文件的命令之前FILES将被设置为“从ls输出”。 (虽然Kaz注意到这个variables每次都被重新扩展,所以最终会包含.tgz文件;为了避免这种情况,为了提高效率和/或正确性,一些变体有FILES := ... 1

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

 all: FILES="$(shell ls)" 

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

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

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

  echo * 

作为你的shell命令。

最后,作为一般规则(不适用于这个例子):作为注释中的世界语笔记,使用来自ls的输出不是完全可靠的(一些细节取决于文件名,有时甚至是ls的版本;某些版本的ls尝试在某些情况下消毒产量)。 因此,如l0b0和idelic所示 ,如果你使用的是GNU,你可以使用$(wildcard)$(subst ...)来完成内部的一切(避免文件名中的“奇怪字符”问题)。 (在sh脚本中,包括makefile的配方部分,另一种方法是使用find ... -print0 | xargs -0来避免跳过空格,换行符,控制字符等等。


1 GNU Make文档还注意到POSIX在2012年增加了::=赋值 。 我还没有find一个POSIX文档的快速参考链接,我也不知道哪些变体支持::=赋值,虽然GNU make今天做的,具有相同的含义:= ,即做任务现在扩大。

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

另外,除了torek的回答之外,还有一点很突出,就是你正在使用懒惰评估的macros分配。

如果您使用的是GNU Make,请使用:=赋值而不是= 。 这个赋值使得右侧立即被扩展,并存储在左侧variables中。

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

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