首页 > 解决方案 > 如何从包含管道和变量的命令中设置变量

问题描述

我有以下制作文件:

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

解决方案


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

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

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

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

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

echo IP: $(MY_IP)

这使得逐步扩展(递归)成为的配方:

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

接着:

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

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

正如您所注意到的,该shell函数解决了所有这些问题,但最终得到了一个可怕的混合:一个 shell 命令,由 make 通过shell函数进行评估,由于 make 函数,它的结果分配给了一个 make 变量eval,在一个配方的中间是一个 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)

如果您有兴趣,请阅读他关于此的帖子$该命令需要多少awk个作为练习留下......


推荐阅读