Makefile中的多行bash命令


120

我有一种非常舒适的方法,可以通过几行bash命令来编译我的项目。但是现在我需要通过makefile对其进行编译。考虑到每个命令都在自己的shell中运行,我的问题是在makefile中运行彼此依赖的多行bash命令的最佳方法什么? 例如,像这样:

for i in `find`
do
    all="$all $i"
done
gcc $all

另外,有人可以解释为什么即使单行命令bash -c 'a=3; echo $a > file'在终端中也能正常工作,而在makefile情况下却创建空文件吗?


4
第二部分应该是一个单独的问题。添加另一个问题只会产生噪音。
l0b0 2012年

Answers:


136

您可以将反斜杠用于行继续。但是请注意,shell接收到整个命令,并将其合并为一行,因此您还需要用分号终止某些行:

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

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

foo:
    gcc `find`

或者,使用更常规的shell $(command)方法(请注意$转义):

foo:
    gcc $$(find)

2
对于多行,您仍然需要转义美元符号,如其他注释中所示。
Tripleee'4

1
是的,简短的回答1. $:= $$ 2.用\ B追加多行bash伙计们通常建议用$$(find)代替`find`调用
Yauhen

89

如问题所示,每个子命令都在其自己的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 ;\
    }

4
您可能还希望SHELL := /bin/bash在makefile中启用特定于BASH的功能,例如进程替换
布伦特·布拉德本

8
微妙之处:后面的空格{对于防止将其解释{set为未知命令至关重要。
布伦特·布拉德本

3
细微之处2:在makefile中,这可能无关紧要,但是如果有时您想要复制脚本并直接从shell提示符运行脚本,则用{}和换行之间的区别将有()很大的不同。您可以通过声明的变量,特别是与修改状态上的shell实例肆虐set,内{}()防止脚本修改您的环境,这可能是首选。示例(这将结束你的shell会话{ set -e ; } ; false
布伦特·布拉德本

您可以通过以下形式添加注释:command ; ## my comment \` (the comment is between ;`和`\`)。这似乎做工精细,只是如果你手动运行该命令(通过复制和粘贴),命令历史记录将包括在某种程度上的评论,打破了命令(如果你试图重复使用)。[注意:由于在反引号内使用了反斜杠,因此该注释的语法突出显示被破坏了。]
Brent Bradburn

如果要在makefile中的bash / perl / script中破坏字符串,请关闭字符串引号,反斜杠,换行符((无缩进))打开字符串引号。例; perl -e QUOTE打印1; QUOTE BACKSLASH NEWLINE QUOTE print 2 QUOTE
mosh

2

仅调用命令有什么问题?

foo:
       echo line1
       echo line2
       ....

对于第二个问题,您需要避免 $使用$$来代替,即bash -c '... echo $$a ...'

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

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

5
原因是“每个命令都在其自身的外壳中运行”,而我需要在command2中使用command1的结果。像例子。
Jofsey 2012年

如果需要在Shell调用之间传播信息,则需要使用某种形式的外部存储,例如临时文件。但是您始终可以重写代码以在同一shell中执行多个命令。
JesperE 2012年

+1,特别是对于简化版。难道就和只做gcc`find`一样吗?
Eldar Abusalimov

1
是的。但是,您当然可以做比“回声”还要复杂的事情。
JesperE

2

当然,编写Makefile的正确方法是实际记录哪些目标取决于哪些源。在平凡的情况下,建议的解决方案将foo依赖于自身,但当然make足够聪明,可以消除循环依赖。但是,如果将临时文件添加到目录中,它将“神奇地”成为依赖关系链的一部分。最好通过脚本一劳永逸地创建显式依赖项列表。

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.