Что вы, вероятно, пропустили, так это то, что параметр 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
был преобразован в $3
командой make при расширении параметра eval
. И это при выполнении:
echo IP: $(MY_IP)
, который make расширяет (рекурсивно) рецепт, который становится, шаг за шагом:
echo IP: `kubectl describe... | awk '{print $3}'`
и затем:
echo IP: `kubectl describe... | awk '{print }'`
(если у вас нет переменной make с именем 3
). Это то, что передается в оболочку, что приводит к тому, что вы видите. Вместо этого используйте $$$$3
, и он должен работать так, как вы ожидаете ... за исключением того, что переменной make MY_IP
не присвоено требуемое значение.
Функция shell
, как вы заметили, решает все это, но в итоге получается ужасная смесь: команда оболочки, вычисляемая make с помощью функции shell
, ее результат присваивается переменной make благодаря eval
функция make, в середине рецепта которой находится список команд оболочки. Я не знаю, что вы хотите сделать, но должно быть что-то попроще.
Например, если вы использовали переменную make только потому, что не могли передать переменную оболочки из одной строки вашего рецепта в другую (обычно они выполняются двумя независимыми вызовами оболочки), вы можете уменьшить свой рецепт до одна строка (но с ; \
продолжением строки для лучшей читаемости):
deploy.runtime:
@MY_IP=`kubectl describe... | awk '{print $$3}'`; \
echo IP: $$MY_IP
Здесь MY_IP
- это переменная оболочки, и она все еще доступна команде 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. Только для 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
, оставлено в качестве упражнения ...