在 makefile 中,我使用 subst 为 make 变量赋值,并且它在 subst 参数中使用另一个 make 变量,但它并没有真正按预期工作:
USER_HOME := $(shell echo ~$$SUDO_USER)
EXP_VAR := $(subst $$HOME_PATH,$(USER_HOME),"$$HOME_PATH/project/dir"))
all:
@echo $(USER_HOME)
@echo $(EXP_VAR)
make 的输出是:
/home/me
~/project/dir
而不是预期的:
/home/me
/home/me/project/dir
所以看来变量
$(USER_HOME)
被正确分配了值/home/me
,但在 subst 函数中使用时错误地分配了~
在 subst 函数的演示中,我没有看到任何解释 make 变量的特殊处理的内容:https://www.gnu.org/software/make/manual/html_node/Text-Functions.html
我可以想象 subst 函数不使用
$(USER_HOME)
的值,而是再次运行 $(shell echo ~$$SUDO_USER)
命令,但肯定有一些东西阻止了 shell 命令的扩展,所以它只输出文字字符串~$$SUDO_USER
并且无法消耗 $SUDO_USER
所以最终变成 ~
我可以用我在这里找到的更复杂的命令来解决问题https://unix.stackexchange.com/a/247582,但我不明白为什么我首先需要它:
USER_HOME := $(shell eval echo "~$$SUDO_USER")
在这种情况下,如果我理解正确的话:
$$
:USER_HOME := $(shell eval echo "~$SUDO_USER")
~$SUDO_USER
/home/me
$(USER_HOME)
和 subst 函数但是为什么 subst 函数不使用 make 变量
$(USER_HOME)
的值,而是再次扩展 $(shell echo ~$$SUDO_USER)
命令?
还有,但不是那么重要的是,为什么 subst 函数中的展开式的工作方式不同?
你做了很多有问题的事情,导致你感到困惑。
首先,当您尝试调试 makefile 时,切勿使用
@
来隐藏配方的 make 输出。这就像将编译器的输出发送到 /dev/null
。这始终是调试 Makefile 的第一条规则。
其次,当您通过
echo
显示变量时,应该始终引用它们。否则 shell 会获取它们并为您操作它们,这样您就看不到真正的内容。例如,您应该使用 echo '$(USER_HOME)'
而不是 echo $(USER_HOME)
。
第三,您应该只使用像
$(info ...)
这样的内置make函数来显示变量的值,而不是像echo
这样的shell命令,因为在shell的调用和生成的输出之间,您无法分辨真正的内容是什么值是。例如,您应该说 $(info USER_HOME is $(USER_HOME))
或类似的内容。
如果您执行了上述所有操作,您对正在发生的事情的理解将会非常不同(注意,我忽略了示例第二行中额外的闭括号的语法错误:我假设这是某种剪切和粘贴神器)。
事实上,这一行:
USER_HOME := $(shell echo ~$$USER)
(假设环境变量
$USER
设置为me
)将USER_HOME
的值设置为~me
,而不是/home/me
。还有这一行:
EXP_VAR := $(subst $$HOME_PATH,$(USER_HOME),"$$HOME_PATH/project/dir")
将
EXP_VAR
的值设置为 "~me/project/dir"
(请注意这里的双引号! make 在几乎任何情况下都不知道或关心引号;到目前为止,它们只是另一个字符,如 a
或 b
就 make 而言)。
现在,如果您查看配方中运行的内容(删除
@
命令之前的 echo
),您将看到它运行:
echo ~me
echo "~me/project/dir"
第一行的输出是
/home/me
,第二行的输出是 ~me/project/dir
,因为字符串被引用,shell 不会对文本进行波形符扩展。
@MadScientist,我的朋友,你能对我的问题添加一些评论吗? 简单的扩展变量赋值有多重要?