Makefile 中的多行 bash 命令

考虑到每个命令都在自己的 shell 中运行,在 makefile 中运行多行 bash 命令的最佳方式是什么?例如,像这样:

for i in `find`
do
all="$all $i"
done
gcc $all
173816 次浏览

只调用命令有什么不对吗?

foo:
echo line1
echo line2
....

对于第二个问题,您需要使用 $$来转义 $,也就是 bash -c '... echo $$a ...'

编辑: 您的示例可以重写为一行脚本,如下所示:

gcc $(for i in `find`; do echo $i; done)

可以使用反斜杠作为行的延续。但是请注意,shell 接收连接到一行中的整个命令,因此您还需要用分号结束一些行:

foo:
for i in `find`;     \
do                   \
all="$$all $$i"; \
done;                \
gcc $$all

但是,如果只想获取由 find调用返回的整个列表并将其传递给 gcc,那么实际上不一定需要多行命令:

foo:
gcc `find`

或者,使用一种更加传统的 shell 方法 $(command)(注意 $的转义) :

foo:
gcc $$(find)

当然,编写 Makefile 的正确方法是实际记录哪些目标依赖于哪些源。在这种简单的情况下,提出的解决方案将使 foo依赖于它自己,但是当然,make足够聪明,可以去掉循环依赖。但是如果您将一个临时文件添加到您的目录中,它将“神奇地”成为依赖链的一部分。最好是一劳永逸地创建一个明确的依赖项列表,也许可以通过脚本实现。

GNU make 知道如何运行 gcc来从一组 .c.h文件中生成一个可执行文件,所以也许您真正需要的是

foo: $(wildcard *.h) $(wildcard *.c)

如问题所示,每个子命令都在自己的 shell 中运行。这使得编写非平凡的 shell 脚本变得有点麻烦—— 但这是有可能的!解决方案是将脚本合并到 make 将考虑的单个子命令(单行)中。

在 makefile 中编写 shell 脚本的技巧:

  1. 通过用 $$替换,避免脚本使用 $
  2. 通过在命令之间插入 ;,将脚本转换为单行工作
  3. 如果要在多行上编写脚本,请使用 \转义行尾
  4. 可以选择从 set -e开始,以匹配 make 的规定,在子命令失败时中止
  5. 这完全是可选的,但是您可以用 (){}将脚本括起来,以强调多行序列的内聚性——这不是典型的 makefile 命令序列

下面是一个受 OP 启发的例子:

mytarget:
{ \
set -e ;\
msg="header:" ;\
for i in $$(seq 1 3) ; do msg="$$msg pre_$${i}_post" ; done ;\
msg="$$msg :footer" ;\
echo msg=$$msg ;\
}

ONESHELL 指令允许编写多行菜单,以便在同一个 shell 调用中执行。

all: foo


SOURCE_FILES = $(shell find . -name '*.c')


.ONESHELL:
foo: ${SOURCE_FILES}
FILES=()
for F in $^; do
FILES+=($${F})
done
gcc "$${FILES[@]}" -o $@

但是有一个缺点: 对特殊前缀字符(‘@’、‘-’和‘ +’)的解释是不同的。

Https://www.gnu.org/software/make/manual/html_node/one-shell.html