makefile 中的多行 bash 命令

问题描述 投票:0回答:6

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

for i in `find`
do
    all="$all $i"
done
gcc $all
bash makefile
6个回答
198
投票

您可以使用反斜杠来续行。但是请注意,shell 会接收到连接成一行的整个命令,因此您还需要用分号终止某些行:

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

但是,如果您只想获取

find
调用返回的整个列表并将其传递给
gcc
,您实际上不一定需要多行命令:

foo:
    gcc `find`

或者,使用更传统的 shell

$(command)
方法(注意
$
转义):

foo:
    gcc $$(find)

131
投票

如问题所示,每个子命令都在自己的 shell 中运行。这使得编写重要的 shell 脚本有点混乱——但这是可能的! 解决方案是将您的脚本合并为 make 将考虑的单个子命令(单行)。

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

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

19
投票

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


3
投票

当然,编写 Makefile 的正确方法是实际记录哪些目标依赖于哪些源。在一般情况下,所提出的解决方案将使

foo
依赖于自身,但当然,
make
足够聪明,可以消除循环依赖。但是如果你在你的目录中添加一个临时文件,它将“神奇地”成为依赖链的一部分。最好一劳永逸地创建一个明确的依赖项列表,也许可以通过脚本。

GNU make 知道如何运行

gcc
以从一组
.c
.h
文件中生成可执行文件,所以也许您真正需要的只是

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

1
投票

只调用命令有什么问题?

foo:
       echo line1
       echo line2
       ....

对于你的第二个问题,你需要使用

$
来逃避
$$
,即
bash -c '... echo $$a ...'
.

编辑:您的示例可以重写为这样的单行脚本:

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

0
投票

在查找如何检测 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
© www.soinside.com 2019 - 2024. All rights reserved.