如何通过包含管道和变量的命令在make中设置变量

时间:2019-06-24 16:05:54

标签: makefile

我有以下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似乎没有得到正确的论点感到非常困惑。我敢肯定这与转义参数有关,但我一生都看不到。

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

您可能会错过的是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在扩展$3参数时已通过make在eval中进行了转换。它是在执行时:

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变量多亏了eval 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 this。作为练习,您需要多少$awk命令才能使用。