您可能错过的是 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 this。 awk 命令需要多少个$ 留作练习...