【问题标题】:How to set variables in make from commands containing pipes and variables如何从包含管道和变量的命令中设置变量
【发布时间】:2019-11-06 11:34:10
【问题描述】:

我有以下 make 文件:

deploy.runtime:
    kubectl describe service hello-node -n default | grep "LoadBalancer Ingress:" | awk '{print $$3}'
    $(eval MY_IP=$(kubectl describe service hello-node -n default | grep "LoadBalancer Ingress:" | awk '{print $$3}'))
    @echo IP: $(MY_IP)

当我运行它时,我得到的输出是:

35.198.222.110
IP: 

似乎未设置可转换的 MY_IP。我也试过用这样的反引号来运行它:

$(eval MY_IP=`kubectl describe service hello-node -n default | grep "LoadBalancer Ingress:" | awk '{print $$3}'`)

这给了我输出:

IP: LoadBalancer Ingress: 35.198.222.110

我真的很困惑为什么 awk 似乎没有得到正确的论点。我确定这与逃生论点有关,但我一辈子都看不出来。

【问题讨论】:

标签: makefile


【解决方案1】:

我发现我可以通过在命令中添加一个“shell”来解决这个问题:

$(eval MY_IP=$(shell kubectl describe service hello-node -n default | grep "LoadBalancer Ingress:" | awk '{print $$3}'))

【讨论】:

    【解决方案2】:

    您可能错过的是 eval 参数是由 make 扩展的。所以,在:

    $(eval MY_IP=$(kubectl describe service hello-node -n default | grep "LoadBalancer Ingress:" | awk '{print $$3}'))
    

    $(kubectl describe...) 被 make 扩展为空字符串(除非你有一个名为 kubectl describe... 的 make 变量,这不太可能)。 eval 函数因此将空字符串分配给 make 变量 MY_IP。反引号不会发生这种情况,但在这里您遇到了另一个问题:分配了 MY_IP 变量:

    `kubectl describe... | awk '{print $3}'`
    

    不是外壳对其进行评估的结果。请注意,$$3 在扩展 eval 参数时已由 make 转换为 $3。并且是在执行时:

    echo IP: $(MY_IP)
    

    make 逐步扩展(递归)成为的配方:

    echo IP: `kubectl describe... | awk '{print $3}'`
    

    然后:

    echo IP: `kubectl describe... | awk '{print }'`
    

    (除非您有一个名为 3 的 make 变量)。这是传递给外壳的内容,导致您看到的内容。请改用$$$$3,它应该可以按您的预期工作...除了make 变量MY_IP 没有分配您想要的值。

    shell 函数,正如你所注意到的,解决了所有这些问题,但你最终得到了一个可怕的混合:一个 shell 命令,由 make 通过shell 函数评估,它的结果分配给一个 make 变量,这要归功于 @ 987654342@ make 函数,在一个配方的中间,它是一个 shell 命令列表。我不知道你想做什么,但一定有更简单的。

    例如,如果您使用 make 变量只是因为您无法将 shell 变量从配方的一行传递到下一行(正常情况下,它们由两个独立的 shell 调用执行),您可以将配方简化为一行(但为了更好的可读性,使用; \ 续行):

    deploy.runtime:
        @MY_IP=`kubectl describe... | awk '{print $$3}'`; \
        echo IP: $$MY_IP
    

    这里,MY_IP 是一个 shell 变量,它仍然可用于 echo 命令,因为它是同一配方行的一部分。请注意 $$$3$$MY_IP 中的双 $ 符号,以逃避 make 的第一个扩展。

    如果您确实想使用 make 变量,则可以将其分配为常规 make 变量(使用反引号或 shell 函数,如您所愿):

    MY_IP = `kubectl describe... | awk '{print $$3}'`
    
    deploy.runtime:
        @echo IP: $(MY_IP)
    

    或:

    MY_IP = $(shell kubectl describe... | awk '{print $$3}')
    
    deploy.runtime:
        @echo IP: $(MY_IP)
    

    关于最后一个解决方案的重要说明:

    MY_IP = ... 赋值是递归(延迟)赋值,而不是简单(立即)赋值MY_IP := ...。这意味着当您调用 make 时,shell 命令不会立即展开和执行。只有当make 需要MY_IP 的值时。因此,如果deploy.runtime 配方是唯一引用MY_IP 值的地方,则只有在执行此配方时才会执行kubectl...。例如,如果您有另一个 clean 目标,该配方不使用 MY_IP 的值,并且如果您调用 make clean,则根本不会执行 kubectl...。缺点是如果你有几个地方 make 需要它的值,kubectl... 将被执行多次。

    如果您使用简单赋值,则改为:

    MY_IP := $(shell kubectl describe... | awk '{print $$3}')
    

    kubectl 命令只会执行一次,但每次调用 make 时都会执行一次,即使它的值不需要。

    如果这是一个问题,Mad Scientist 有一个很好的技巧来结合递归和简单赋值的优缺点:

    MY_IP = $(eval MY_IP := $$(shell kubectl...))$(MY_IP)
    

    如果您有兴趣,请阅读his post about thisawk 命令需要多少个$ 留作练习...

    【讨论】:

      猜你喜欢
      • 2016-05-12
      • 2019-08-23
      • 2022-07-01
      • 1970-01-01
      • 2018-03-20
      • 2020-06-06
      • 2013-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多