我如何在Makefile目标中使用Bash语法?

276

我经常发现Bash语法非常有用,例如进程替换,比如diff <(sort file1) <(sort file2)

在Makefile中是否可以使用这样的Bash命令?我想到了这样的东西:

file-differences:
    diff <(sort file1) <(sort file2) > $@

在我的GNU Make 3.80中,这将会出现错误,因为它使用sh而不是bash来执行命令。

2
这正是我的问题,我花了至少一个小时才找到这个问题!我在这里留下我的错误信息,以便未来的读者可以找到它:/bin/sh: -c: line 0: syntax error near unexpected token ('`。 - David
7个回答

509

来自GNU Make文档

5.3.2 Choosing the Shell
------------------------

The program used as the shell is taken from the variable `SHELL'.  If
this variable is not set in your makefile, the program `/bin/sh' is
used as the shell.

在您的makefile顶部添加SHELL:= /bin/bash,然后您就可以开始了。

顺便说一句:您也可以为一个目标执行此操作,至少对于GNU Make而言。每个目标都可以有自己的变量分配,如下所示:

all: a b

a:
    @echo "a is $$0"

b: SHELL:=/bin/bash   # HERE: this is setting the shell for b only
b:
    @echo "b is $$0"

这会输出:

a is /bin/sh
b is /bin/bash

请参阅文档中的“目标特定变量值”了解更多详细信息。该行可以放置在Makefile的任何位置,不必紧接在目标之前。


64
有一份500赏金的任务需要man提供报价。谈论一下时间。 :P - SiddharthaRT
3
嗯,其实是关于“信息”的,但我猜这确实帮到了安迪。我知道我也有过那样的日子...... - derobert
3
如果有疑问,@derobert 的意思是在 Makefile 的第一行(或注释后面)写上 SHELL=/bin/bash - Yauhen Yakimovich
2
如果您使用 /usr/bin/env bash,它将使用 PATH 中的 bash。但如果仅是 SHELL := bash 呢? - CMCDragonkai
2
前面的评论是不正确的。$(info $(SHELL))将始终显示make将调用的shell,而不是调用shell的SHELL环境变量的值。如果info打印/bin/bash,那么这意味着在您的makefile中的某个地方,您已经将makefile变量SHELL设置为/bin/bash,并且这就是make在调用recipes时使用的shell。 - MadScientist
显示剩余2条评论

26

您可以直接调用bash,使用-c标志:

bash -c "diff <(sort file1) <(sort file2) > $@"

当然,你可能无法将重定向指向变量$@,但是当我尝试这样做时,我得到了-bash:$@:ambiguous redirect的错误消息,所以在深入研究之前,你可能需要注意一下这个问题(虽然我使用的是bash 3.2.x版本,所以你的版本可能会有所不同)。


6

还有一种方法也可以使用,就是在目标代码的第一行这样放置:

your-target: $(eval SHELL:=/bin/bash)
    @echo "here shell is $$0"

4

您可以在Makefile中直接调用bash,而不使用默认的shell:

bash -c "ls -al"

改为:

ls -al

1
请注意,make 忽略环境变量 SHELL 的值。 - choroba

4

如果可移植性很重要,您可能不想在Makefile中依赖特定的shell。并非所有环境都有bash可用。


1
有一种方法可以在不明确设置SHELL变量以指向bash的情况下实现这一点。如果您有很多个makefile,这将非常有用,因为SHELL不会被后续的makefile继承或从环境中获取。您还需要确保编译您代码的任何人都以这种方式配置了系统。
如果您运行“sudo dpkg-reconfigure dash”并在提示时回答“否”,则系统将不使用dash作为默认shell。然后它将指向bash(至少在Ubuntu中)。请注意,使用dash作为系统shell可能更加高效。

2
当以sh的名称调用时,Bash以兼容模式(set -o posix)运行。 OP试图使用的功能——进程替换,在此模式下不可用。 - Charles Duffy

0

这不是对问题的直接回答,makeit 是一个带有 bash 语法的受限 Makefile 替代品,在某些情况下可能很有用 (我是作者)

  • 规则可以定义为 bash 函数
  • 自动完成功能

基本思想是在脚本末尾使用 while 循环:

while [ $# != 0 ]; do
  if [ "$(type -t $1)" == 'function' ]; then
    $1 
  else
    exit 1
  fi 
  shift
done

https://asciinema.org/a/435159


2
顺便提一下,按照规定,在你的回答中披露你是链接项目的作者。 - Qix - MONICA WAS MISTREATED

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