如何在Makefile配方中使用for循环

12

我想使用循环查找一些文件并将它们重命名:

  for i in `find $@  -name *_cu.*`;do mv $i "$(echo $i|sed s/_cu//)"
  done

这个命令在终端中可以正常工作。但是我该如何在makefile规则中执行它呢?


1
离题:在find命令中引用'*_cu.*'是个好主意,因为你想让find展开它,而不是调用find的shell展开它。 - John Marshall
这个命令的触发对于你的目标 $@ 不会正常工作。 - reinierpost
3个回答

35

将非平凡的 shell 片段放入 makefile 的配方中时,有两个主要的事情需要知道:

  1. 配方中的命令(当然!)是逐一执行的,其中 command 意味着“在配方中以制表符为前缀的行”,可能会在多个 makefile 行上使用反斜杠分隔。

    因此,您的 shell 片段必须写在一个(可能带有反斜杠)行上。而且它被有效地呈现给 shell 作为单行(反斜杠换行不是普通的换行符,因此不能被 shell 用作命令终止符),因此必须作为语法正确的单行。

  2. shell 变量和 make 变量都由美元符号($@$i)引入,因此您需要通过将其编写为 $$i 来将 shell 变量隐藏在 make 中。(更精确地说,任何您希望 shell 看到的美元符号都必须被从 make 中转义,方法是将其编写为 $$。)

在 shell 脚本中通常会将不同的命令写在不同的行上,但在这里您实际上只能得到一行命令,因此必须使用分号分隔各个单独的 shell 命令。将所有这些组合应用于您的示例,将产生以下结果:

foo: bar
    for i in `find $@  -name *_cu.*`; do mv $$i "$$(echo $$i|sed s/_cu//)"; done

或者等价地说:

foo: bar
    for i in `find $@  -name *_cu.*`; do      \
        mv $$i "$$(echo $$i|sed s/_cu//)";    \
    done

请注意,即使后面的代码在几行上排列得很清晰,也需要谨慎地使用分号才能让shell正常运行。


出于好奇,你为什么要保留 $@?在 Makefile 中它不是有特殊的含义吗?在 shell 中,它会扩展成脚本参数列表... - fge
@fge:make将命令传递给shell,就像通过system()一样,因此没有shell脚本参数,shell的$@始终为空。另一方面,make的$@是指规则的目标(这里是_foo_),我假设这就是预期的意图。 - John Marshall
1
好的,我不是很确定,OP中的片段似乎确实来自一个shell脚本。 - fge

5

我发现这个非常有用,尝试使用for循环来构建多个文件:

PROGRAMS = foo bar other

.PHONY all
all: $(PROGRAMS)
$(PROGRAMS):
    gcc -o $@ $@.c

它将编译foo.c、bar.c和other.c成为foor、bar、other可执行文件。

0

我花了很多时间,最终让它工作了。我在makefile中使用全局变量有一个简单的解决方案,适用于所有目标,但我不想这样做,所以我是这样做的。

target:
    $(eval test_cont=$(shell sh -c "docker ps | grep test" | awk '{print $$1}'))
    for container in $(test_cont);do \
        docker cp ssh/id_rsa.pub $${container}:/root/.ssh/authorized_keys; \
        docker exec -it $${container} chown root.root /root/.ssh/authorized_keys; \
    done

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