如何在Makefile中正确转义数据?

55

我正在使用一个Bash脚本动态生成config.mk文件,并将其用于Makefile。该文件是通过以下方法构建的:

cat > config.mk <<CFG
SOMEVAR := $value_from_bash1
ANOTHER := $value_from_bash2
CFG

如何确保生成的文件实际上包含了$value_from_bash*的内容,而不是被展开/解释的内容?我可能需要将$转义为$$,将\转义为\\,但是否还有其他需要转义的字符?也许有一种特殊的字面赋值方式我没有听说过?

空格似乎也会引起问题:

$ ls -1
a b
a
$ cat Makefile
f := a b
default_target:
    echo "$(firstword $(wildcard ${f}))"
$ make
a
如果我使用f := a\ b,它可以工作(使用引号如f := 'a b'也不行,makefile只会将其视为常规字符)。

你使用的 Make 版本是什么? - Beta
1
这不是一个Makefile的问题,而是一个bash的问题。我想你知道你想让你的makefile看起来像什么,所以你的问题是在bash的here-document中转义字符。请参考这个网址:http://tldp.org/LDP/abs/html/here-docs.html。 - mb14
1
不,我不是在问如何在bash中替换字符串,而是想知道在Makefile中获取bash字符串而不修改其含义的方法(例如,如果echo“$var”显示“whatever '”,则Makefile中应显示相同的内容)。 - Lekensteyn
3个回答

85

好的,事实证明Makefile本身只需要进行少量的转义,但是由shell解释器执行的命令必须被转义。

在Makefile中具有特殊意义需要转义的字符有:

  • 井号(#,注释)变成 \#
  • 美元符号($,变量开始)变成 $$

不能在变量中插入换行符,但为了避免破坏其余的Makefile,请在其前面加上反斜杠,这样换行符将被忽略。

很遗憾,反斜杠本身无法转义(\\仍然是\\而不是像你期望的那样成为\)。这使得无法在字符串末尾放置一个文字斜杠,因为它会吞噬换行符或后续注释的哈希值,可以在行末放置一个空格,但也会放入变量本身。

该配方本身被解释为一个shell命令,没有任何花哨的转义,因此您必须自己转义数据,想象一下您正在编写一个shell脚本并插入其他文件中的变量。在此处的策略是将变量放在单引号之间,只转义''\''(关闭字符串,插入一个文字',并开始一个新的字符串)。例如:mornin' all 变成 'morning'\'' all' ,它等同于 "morning' all"

firstword+wildcard问题是由于带有空格的文件名被视为firstword的单独文件名所致。此外,wildcard使用\扩展转义,因此x\ y匹配为一个字,即x y而不是两个字。


反引号也要翻译:backtick :) - lifeofguenter
相关博客文章显示在bash中的$必须在make中转义为$$:https://til.hashrocket.com/posts/k3kjqxtppx-escape-dollar-sign-on-makefiles - Gabriel Staples
1
并且 % 变成 %% - forzagreen

10
这个问题的完整答案似乎在互联网上找不到,所以我最终花时间为Windows情况解决了这个问题。
具体来说,“Windows情况”指的是在Windows中有效的文件名,这意味着它们不包含字符\, /, *, ?, ", ^, <, >, |或换行符。同时,对于Make来说,\/都被视为有效的目录分隔符。
通过示例更容易理解。基本上,如果您正在尝试匹配此文件路径:
Child\a$b  {'}(a.o#$@,&+=~`),[].c

然后您需要编写这些规则:
all: Child\\a$$b\\\ \\\ {'}(a.o\#$$@,&+=~`),[].o

%.o: %.c
    $(CC) '$(subst ','"'"',$(subst \,,$(subst \\,/,$+)))'

长时间盯着它,它会开始变得有点让人能够理解。这在我的MSYS2环境下有效,因此我认为它是正确的。

那个替换模式看起来很奇怪,语法上是有效的吗?它似乎将 ' 替换为 '"'"'(在将 \ 替换为 / 后),这正确吗?噢,基本上你是将 ' 替换为 ',然后是一个带引号的字面量 "'",然后继续单引号,这些单引号从参数前后开始。你有一些关于这种转义的参考资料吗? - Lekensteyn
@Lekensteyn:是的,https://dev59.com/hHM_5IYBdhLWcg3wvF3J#1250279。然而,那只是整个问题中最简单的部分。真正的困难在于不要弄错空格和其他字符,我认为我的答案仍然在某些情况下失败了…… 我将在有更好的答案时更新它。 - user541686
啊,是的,那是 sh 风格的转义字符,不一定只用于 Windows 命令。如果我没记错的话,在 Windows(批处理脚本)中使用 ^ 作为转义字符。 - Lekensteyn
@Lekensteyn:实际上这不是针对批处理脚本,而是针对 sh 风格的转义字符……但是在 Windows(MSYS2)上,而不是 Linux。抱歉造成困惑,我发布答案的主要原因是我自己也在寻找它,而不是我认为它会有助于你的情况。 - user541686
是的,它可以工作,但前提条件被认为是不存在的,会进行冗余编译。 - Tuff Contender

2
  1. 我不明白你所说的那个makefile是如何工作的。一个模式规则不能成为默认规则。
  2. 在`$(wildcard ...)`中,你缺少了一个`$`,因此我认为你没有发布你真正测试的内容。
  3. 你应该也要转义换行符。

谢谢你的回答,我在制作示例时确实打错了一个字。只转义换行符、空格、美元符号和反斜杠就足够了吗? - Lekensteyn
@Lekensteyn,我也这么认为。但你可能需要重新考虑你的设计;Make从来没有真正用于强大的字符串操作。 - Beta
变量实际上是文件路径。通常,它们不包含像换行符这样的特殊字符(因此我将忽略它),$(但你永远无法确定)。需要注意的可能特殊字符是 ", ' 和空格。 - Lekensteyn
你能否通过总结需要转义的字符列表来改进答案? - Lekensteyn

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