在Makefile中的Shell循环

3
我写了一个Makefile,希望它能按照以下顺序生成文件。
1.out
2.out
3.out

makefile

TOUCH := $(shell for n in 'seq 1 3'; do echo $$n ;done)
.PHONY: test
test:
    @$(TOUCH)

[OK] 的结果:

1
2
3

然而,当我将 echo 替换为 touch 时,
TOUCH := $(shell for n in 'seq 1 3'; do touch $$n.out ;done)

[错误] make 结果:

1
seq
3.out

1
如果你想这样使用make,为什么不直接编写一个shell脚本呢? - reinierpost
2个回答

8

您似乎在混淆两个完全不同的结构。

VARIABLE := value

value赋值给变量。

VARIABLE := $(shell echo value)

将使用shell产生一个字符串,然后将该字符串赋值给VARIABLE。这发生在解析Makefile之前,即在Make尝试实际执行任何配方之前。

我想您可能正在寻找以下内容:

TOUCH := for n in $$(seq 1 3); do touch $$n.out; done
.PHONY: test
test:
        @$(TOUCH)

当然也可以将其内联;

.PHONY: test
test:
        @for n in $$(seq 1 3); do touch $$n.out; done

一个更好的设计是让Make负责,尽管如此:
.PHONY: test
test: 1.out 2.out 3.out
%.out:
        @touch $@

(个人建议,去掉@前缀,这样你可以清楚地看到发生了什么。如果输出会影响你,请在调试完成后使用“make -s”运行。)

VARIABLE := $(shell bla bla bla) 将使用 shell 命令生成一个字符串。谢谢。现在更清楚了。但是,touch 版本只是将命令从 echo 更改为 touch 并添加一个 .out 作为后缀。为什么结果完全不同? - nwpie
1
我认为您没有正确转录引号。如果第一个命令使用单引号,它也无法正常工作。我猜您最初使用了正确的反引号,然后错误地将它们更改为普通的直撇号,甚至没有注意到。我相信您可以通过复制+粘贴您在此处编写的内容并再次尝试来验证这一点。 - tripleee
我再次尝试了 echo 命令。单引号或双引号都无所谓,但当我执行 echo $$n.out 时,它显示了 "seq: invalid floating point argument: 3.out",这意味着在这种情况下,$$n 只能接受数字格式。 - nwpie
这完全不是正确的分析。错误信息意味着您得到了类似于 $(seq 1 3.out) 而不是 $(seq 1 3) 这样的结果。 - tripleee
1
这不是单引号和双引号的问题,而是反斜杠和单引号的问题。 - tripleee
显示剩余3条评论

2
这里有几个问题需要解决。
在你的shell代码中,你使用了单引号而不是反引号。单引号创建字符串,而反引号执行命令。
因此,当你写for n in 'seq 1 3'; do echo $$n; done时,shell正在循环遍历单个字符串"seq 1 3"的列表,而不是数字1、2和3。
这导致TOUCH的值为"seq 1 3"。
然后,当你在规则@$(TOUCH)中扩展TOUCH时,该行变成@seq 1 3,然后shell会为你执行并输出结果。
1
2
3

但不是你想的那个原因。(去掉前面的@,你就能看到它。)
鉴于这些信息,如果我们看一下不工作的示例。
TOUCH := $(shell for n in 'seq 1 3'; do touch $$n.out ;done)

并且取出循环体touch $$n.out,将$$n替换为字符串“seq 1 3”,得到touch seq 1 3.out

这样我们就得到了一个$(shell)调用,它运行touch seq 1 3.out并创建你在帖子中列出的文件和一个空的TOUCH变量。这也解释了为什么(尽管您没有询问)在运行make时会得到make: Nothing to be done for 'test'.的输出。

我还要明确指出,您的echo/touch运行是在$(shell)调用期间而不是test规则执行期间进行的。这可能不是您想要的。要么不使用简单扩展:=变量,要么将要运行的命令放入变量中,不使用$(shell)


给你的分析点个赞,但它的价值远不止于此。SO新规定禁止在评论开头写+1,这是很荒谬的命令,因此我们会尽可能地忽略和滥用它。 - Jonathan Leffler
谢谢解释。但实际上,“for n in 'seq 1 3'; do echo $$n; done”将按顺序输出1、2、3。为什么会这样? - nwpie
这不行,那些引号是不行的。使用正确的反引号,命令 for n in \seq 1 3`; do ...(也可以更清晰、更可移植地写成 for f in $(seq 1 3); do ...)在 GNU Make 语法下执行的结果与你期望的 for n in $(shell seq 1 3)` 差不多。 - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接