在Makefile的Bash Shell变量中是否应使用双美元符号?
测试场景回顾
初始测试
- Makefile内容:
sgr0 := $(shell tput sgr0) bold := $(shell tput bold) orange := $(shell tput setaf 166) test: @echo "$(bold)$(orange)ENV_VARIABLE:$(sgr0) ${ENV_VARIABLE}"
- 执行步骤:
- 在Bash中执行:
export ENV_VARIABLE=value - 运行
make test,输出:ENV_VARIABLE: value
- 在Bash中执行:
修改后测试
将Makefile的test目标修改为:
test: @echo "$(bold)$(orange)ENV_VARIABLE:(sgr0) $${ENV_VARIABLE}"
运行make test后,输出:
ENV_VARIABLE: $ENV_VARIABLE
更新测试
- 执行:
export ENV=test.txt - Makefile内容:
test: touch $(ENV)
此配置可正常执行touch test.txt;若改为$$(ENV)则会报错。
核心问题
根据GNU Make官方文档,Shell变量需使用$$,Make变量使用$,但实际测试中用$$无法展开环境变量,反而直接用$可以正常工作,疑惑如何遵循规范正确展开Shell环境变量?
问题解析
这里的关键是区分Make的变量展开阶段和Shell的执行阶段,以及GNU Make的环境变量导入特性:
Make自动导入环境变量
GNU Make默认会将当前Shell的所有环境变量导入为自身的全局变量。所以当你在Makefile的命令中写$(ENV)或${ENV_VARIABLE}时,Make会直接替换为对应的环境变量值,再把替换后的命令传递给Shell执行——这就是你前两个测试中用$能正常展开的原因,本质是Make替你完成了变量展开。$$的实际作用
文档中提到的“Shell变量用$$”,指的是Shell本地变量(比如在Shell命令里定义的local var=xxx)或者需要由Shell而非Make展开的变量。$$的作用是让Make把单个$传递给Shell,避免Make对其进行解析。
举个例子,如果你要在Shell中使用本地变量,需要这么写:
test: @local shell_var="hello world"; echo "$$shell_var"
此时Make会把$$shell_var转换成$shell_var,Shell执行时就能正确展开这个本地变量。
- 为什么你的
$${ENV_VARIABLE}没生效?
你修改后的测试中输出$ENV_VARIABLE,大概率是因为Makefile里的笔误:你把$(sgr0)写成了(sgr0),导致整个echo字符串的格式出现问题,干扰了Shell对变量的解析。正确的写法应该是:
test: @echo "$(bold)$(orange)ENV_VARIABLE:$(sgr0) $${ENV_VARIABLE}"
修正后,Make会将$${ENV_VARIABLE}转换成${ENV_VARIABLE}传递给Shell,Shell就能正常展开环境变量的值。
- 关于
$$(ENV)报错的原因
当你写$$(ENV)时,Make会把它转换成$(ENV)传递给Shell,而Shell会将$(ENV)解析为命令替换(尝试执行ENV命令),但系统中并没有名为ENV的命令,因此会报错。正确的写法如果想让Shell展开环境变量,应该是$$ENV(不带括号)。
总结
- 如果想让Make帮你展开环境变量,直接使用
$(VAR)或${VAR}即可,这是最常用的方式。 - 如果想让Shell自己展开变量(比如Shell本地变量),使用
$$VAR,确保Make传递正确的$给Shell。
内容的提问来源于stack exchange,提问作者Berstigma

