如何在Makefile中使用Shell命令


99

我正在尝试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),没有运气。

Answers:


149

带有:

FILES = $(shell ls)

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

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

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

当然,这意味着FILES运行创建.tgz文件的任何命令之前将其设置为“从...输出ls” 。(虽然如卡兹注意到变量每次重新扩大,所以最终会包括.tgz的文件;一些化妆变体具有避免这种情况,以提高效率和/或正确性。1FILES := ...

如果FILES应该是shell变量,则可以设置它,但是需要用shell-ese来做,没有空格,并用引号引起来:

all:
        FILES="$(shell ls)"

但是,每一行都由单独的外壳程序运行,因此此变量将无法保留到下一行,因此您必须立即使用它:

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

这有点愚蠢,因为外壳首先会*为您扩展(以及其他外壳glob表达式),因此您可以:

        echo *

作为您的shell命令。

最后,作为一般规则(不适用于此示例):正如世界语在注释中所指出的那样,使用from的输出ls并不完全可靠(某些详细信息取决于文件名,有时甚至取决于的版本ls;某些ls试图清除输出的版本在某些情况下)。因此,如l0b0理想注释所示,如果您使用的是GNU,则可以使用$(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...在缩短变体在更多变体中工作方面具有优势。


谢谢。我想使用复杂的命令(例如,ls带有sedand cut),然后在rsync和其他命令中使用结果。我是否需要一遍又一遍地重复冗长的命令?我不能将结果存储在内部的Make变量中吗?
亚当·马坦

1
Gnu make可能有办法做到这一点,但我从未使用过,我们使用的所有极其复杂的makefile都仅使用shell变量和在每行末尾使用“; \”构建的巨型一线shell命令,如下所示:需要。(无法在此处使用反斜杠序列的代码编码,嗯)
torek 2012年

1
也许像这样: FILE = $(shell ls *.c | sed -e "s^fun^bun^g")
威廉·莫里斯

2
@William:make无需使用shell:就可以做到这一点FILE = $(subst fun,bun,$(wildcard *.c))
Idelic

1
我想指出的是,尽管在这种情况下它似乎不是很重要,但是您不应该自动解析ls的输出。ls旨在向人类显示信息,而不是被束缚在脚本中。此处的更多信息:mywiki.wooledge.org/ParsingLs 可能的情况是,如果'make'不能为您提供适当的通配符扩展,则“ find”胜于“ ls”。
劳尔·萨利纳斯-蒙塔古多

53

另外,除了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命令。这会使您的制作工作变慢,甚至产生一些令人惊讶的后果。


1
现在我们有了列表,我们如何遍历列表中的每个项目并对其执行命令?如构建还是测试?
anon58192932 '10 -10-12

3
@ anon58192932执行命令的特定迭代通常是在构建配方的shell语法片段中完成的,因此它发生在shell中,而不是在make:中 for x in $(FILES); do command $$x; done。注意加倍$$将单个传递$给外壳。而且,贝壳碎片是一线的;要编写多行外壳程序代码,请使用反斜杠连续符,该反斜杠连续符由make自身处理并折叠成一行。这意味着必须使用shell命令分隔分号。
哈兹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.