如何在GNU Make中正确包含/执行命令输出以定义变量?
GNU Make 正确解析Shell命令输出的变量定义
问题场景
有一个生成Make变量定义的Shell脚本test.sh:
echo 'V1 = foo bar' echo 'V2 = ab cd'
直接在Makefile中使用$(eval $(shell ./test.sh))时,由于$(shell)函数会将命令输出的换行符替换为空格,导致变量解析错误:
Makefile代码:
$(eval $(shell ./test.sh)) $(info V1='$(V1)') $(info V2='$(V2)')
错误输出:
V1='foo bar V2 = ab cd' V2=''
期望输出:
V1='foo bar' V2='ab cd'
解决方案
方法1:通过foreach逐行解析输出
利用Make的foreach循环,先将Shell输出按换行拆分,再逐个执行eval处理每行的变量定义:
# 定义换行符(注意中间要留空行) define NL endef # 捕获Shell输出,替换换行为列表分隔符后逐行eval $(foreach line,$(subst $(NL), ,$(shell ./test.sh)),$(eval $(line))) $(info V1='$(V1)') $(info V2='$(V2)')
核心是通过subst将换行符转换为Make列表的空格分隔符,再用foreach遍历每一行内容执行eval,确保每个变量定义被单独解析。
方法2:修改输出格式直接生成eval语句
调整Shell脚本,让输出内容直接包含eval指令,这样Make会自动执行每个变量定义:
修改后的test.sh:
echo '$(eval V1 = foo bar)' echo '$(eval V2 = ab cd)'
对应的Makefile:
$(shell ./test.sh) $(info V1='$(V1)') $(info V2='$(V2)')
如果不想修改原脚本,也可以在Make中通过sed处理输出:
$(shell ./test.sh | sed 's/^/$$(eval /; s/$$/)/') $(info V1='$(V1)') $(info V2='$(V2)')
sed会给每行添加$(eval 前缀和)后缀,让Shell输出直接成为可执行的Make指令。
方法3:临时文件+include(最兼容方案)
如果上述方法存在版本兼容问题,最稳妥的方式是将Shell输出写入临时文件,再用include指令加载:
# 生成临时文件路径 temp_vars := $(shell mktemp) # 将脚本输出写入临时文件 $(shell ./test.sh > $(temp_vars)) # 加载临时文件中的变量定义 include $(temp_vars) # 清理临时文件的目标 .PHONY: clean clean: rm -f $(temp_vars) $(info V1='$(V1)') $(info V2='$(V2)')
这种方法不受$(shell)换行替换的影响,兼容性最好,适合复杂的变量定义场景。
内容的提问来源于stack exchange,提问作者Thierry Lelegard
相关产品推荐
相关产品推荐

