【问题标题】:Makefile Use Shell to create variablesMakefile 使用 Shell 创建变量
【发布时间】:2017-02-18 10:15:27
【问题描述】:

我正在使用 make 的 shell 命令来填充一些变量,并且在输出时它表明它们被设置为我期望的值。然而,当我将它们传递给我的制作食谱时,它们都显示为空。我怀疑 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)'
  • 顺便说一句,cat is useless

标签: bash makefile gnu-make


【解决方案1】:

这里发生了几件事。第一个是当你有:

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

您正在设置一个名为LINEshell 变量,而不是make 变量。目标的构建指令都是 shell 命令。所以在第一行之后,shell 变量$LINE 包含GIMME_THE_LINE_I_WANT。不过……

...构建指令中的每个 line 目标都在单独的 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 ...) 的每次调用也在单独的进程中运行,因此一个变量集在另一个进程中将不可用。)

【讨论】:

    【解决方案2】:

    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 展示他/她如何使用调试选项来解决人们在使用 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}这样简单的方法来试试这个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-09
    • 2012-11-26
    • 2023-01-11
    • 1970-01-01
    • 1970-01-01
    • 2014-01-28
    相关资源
    最近更新 更多