考虑到每个命令都在自己的 shell 中运行,在 makefile 中运行多行 bash 命令的最佳方式是什么?比如像这样:
for i in `find`
do
all="$all $i"
done
gcc $all
您可以使用反斜杠来续行。但是请注意,shell 会接收到连接成一行的整个命令,因此您还需要用分号终止某些行:
foo:
for i in `find`; \
do \
all="$$all $$i"; \
done; \
gcc $$all
但是,如果您只想获取
find
调用返回的整个列表并将其传递给 gcc
,您实际上不一定需要多行命令:
foo:
gcc `find`
或者,使用更传统的 shell
$(command)
方法(注意 $
转义):
foo:
gcc $$(find)
如问题所示,每个子命令都在自己的 shell 中运行。这使得编写重要的 shell 脚本有点混乱——但这是可能的! 解决方案是将您的脚本合并为 make 将考虑的单个子命令(单行)。
$
来逃避脚本对
$$
;
将脚本转换为单行工作\
set -e
开始以匹配 make 的规定以在子命令失败时中止()
或 {}
将脚本括起来以强调多行序列的内聚性——这不是典型的 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
当然,编写 Makefile 的正确方法是实际记录哪些目标依赖于哪些源。在一般情况下,所提出的解决方案将使
foo
依赖于自身,但当然,make
足够聪明,可以消除循环依赖。但是如果你在你的目录中添加一个临时文件,它将“神奇地”成为依赖链的一部分。最好一劳永逸地创建一个明确的依赖项列表,也许可以通过脚本。
GNU make 知道如何运行
gcc
以从一组 .c
和 .h
文件中生成可执行文件,所以也许您真正需要的只是
foo: $(wildcard *.h) $(wildcard *.c)
只调用命令有什么问题?
foo:
echo line1
echo line2
....
对于你的第二个问题,你需要使用
$
来逃避 $$
,即 bash -c '... echo $$a ...'
.
编辑:您的示例可以重写为这样的单行脚本:
gcc $(for i in `find`; do echo $i; done)
在查找如何检测 docker 是否正在从 Makefile 运行时发现这篇文章...没有找到示例,所以我写了一个。这是上下文中的 GNUmakefile ...
https://github.com/RandyMcMillan/make-docker-start
OS :=$(shell uname -s)
export OS
.ONESHELL:
docker-start:## detect whether docker is running...
@( \
while ! docker system info > /dev/null 2>&1; do\
echo 'Waiting for docker to start...';\
if [[ '$(OS)' == 'Linux' ]]; then\
systemctl restart docker.service;\
fi;\
if [[ '$(OS)' == 'Darwin' ]]; then\
open --background -a /./Applications/Docker.app/Contents/MacOS/Docker;\
fi;\
sleep 1;\
done\
)
docker-pull:docker-start## pull alpine image
docker pull alpine