Makefile使用Shell创建变量

12

我正在使用make的shell命令来填充一些变量,在输出时它指示它们被设置为我期望的值。然而,当我将它们传递给我的make配方时,它们都为空。我怀疑make在结果上进行了一些奇怪的解析。例如:

MyTarget: $(MySources)
    LINE='$(shell cat $< | grep GIMME_THE_LINE_I_WANT)'
    CH9=$(shell echo $(LINE) | cut -b9)
    echo $(CH9) # doesn't print anything

我手动检查了我的生成器命令,通过设置SHELL=sh -XV,当我运行相同的命令时,我得到了正确的值,只是看起来bash正在“清零”我的变量。有什么想法吗?


$(LINE) 中是否有特殊字符?你应该将其引用起来,例如 shell echo '$(LINE)' - Barmar
顺便说一句,那个cat是无用的 - tripleee
2个回答

38

这里发生了几件事情。首先当你有:

MyTarget: $(MySources)
    LINE='$(shell cat $< | grep GIMME_THE_LINE_I_WANT)'

你正在设置一个名为LINEshell变量,而不是make变量。目标的构建指令都是shell命令。因此在第一行之后,shell变量$LINE包含GIMME_THE_LINE_I_WANT。然而...

...每个目标构建指令中的都在单独的shell进程中运行。如果你有:

mytarget:
    MYVAR=foo
    echo $$MYVAR

由于$MYVAR在第二个命令的上下文中没有设置,因此您将看不到任何输出。请注意在这里使用$$,否则$会被Make解释(也就是说,写$MYVAR实际上是Make表达式$M后面跟着文本YVAR)。您可以通过将行逻辑连接成单个Shell脚本来解决此问题,如下所示:

mytarget:
    MYVAR=foo; \
    echo $$MYVAR

\符号是Makefile语法,它可以将一行逻辑代码拓展到多个物理行,而;则是Shell语法,用于在一行上组合多个命令。

有了这些知识,我们可以将您的目标重写为:

MyTarget: $(MySources)
    LINE=$$(cat $< | grep GIMME_THE_LINE_I_WANT); \
    CH9=$$(echo $$LINE | cut -b9); \
    echo $$CH9

请注意,由于我们已经在运行一个shell脚本,因此我没有使用Make的$(shell ...)结构,并且我确保转义所有的$字符以确保变量扩展是由shell而不是Make处理。

再进一步说,您不需要在该脚本中使用cat;您可以简单地编写:

MyTarget: $(MySources)
    LINE=$$(grep GIMME_THE_LINE_I_WANT $<); \
    CH9=$$(echo $$LINE | cut -b9); \
    echo $$CH9
或者:
MyTarget: $(MySources)
    CH9=$$(grep GIMME_THE_LINE_I_WANT $< | cut -b9); \
    echo $$CH9

(注:尽管与此解决方案无关,但值得注意的是,每次调用$(shell ...)也会在单独的进程中运行,因此在一个变量中设置的变量在另一个变量中不可用。)


4

Make命令会在独立的shell中运行每个命令,因此值不会被传递。

如果有疑问,您可以始终使用-d选项进行调试。另外,需要注意的是,在您尝试弄清楚规则未按预期触发的原因时,调试选项非常有用。

~> cat Makefile
MyTarget:
        LINE="somevalue"
        echo ${LINE}
~>
~>
~> make -d MyTarget | tail -10
LINE="somevalue"
Putting child 0x2004fbb0 (MyTarget) PID 4052 on the chain.
Live child 0x2004fbb0 (MyTarget) PID 4052
Reaping winning child 0x2004fbb0 PID 4052
echo
Live child 0x2004fbb0 (MyTarget) PID 4192

Reaping winning child 0x2004fbb0 PID 4192
Removing child 0x2004fbb0 PID 4192 from chain.
Successfully remade target file 'MyTarget'.
~>

只是为了澄清那些对我的解决方案投反对票并发表评论说它不起作用的人:首先,我为我的懒惰道歉。也许我应该更清楚。让我再试一次。
“make -d”不是解决OP问题的方法。
我试图向OP展示如何使用debug选项来解决人们在使用makefile时遇到的各种问题(我承认,这与仅解决手头的OP问题有些偏离)。
上述调试显示第一个命令在PID = 4052的shell中执行,第二个命令在另一个PID = 4192的shell中执行(不带该变量的值)。此外,它还显示,使用单个美元符号(${LINE})的变量只会给你一个空白(因为makefile不将其解释为shell变量)。
再次澄清:“make -d”不是解决方案。只需将命令组合在一行中,用逗号分隔,使用双倍美元符号;如果行很长,请转义新行。
   MyTarget:
      LINE="somevalue"; \
      echo $${LINE}

这样做行不通,因为正如larsks所指出的那样,表达式${LINE}将被Make替换而不是shell,并且始终会评估为空值(即使在同一个shell中运行两个命令也是如此)。您可以尝试使用一些简单的内容,例如LINE="somevalue"; echo ${LINE} - ldx.a.ldy.c

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