在GNU Make 3.81中,如何避免Makefile define里Bash for循环变量展开
解决GNU Make中shell变量转义与脚本生成的问题
看起来你在Makefile里生成shell脚本时遇到了变量展开的坑,我来帮你拆解问题并修正:
核心问题分析
你生成的trial.sh里echo内容为空,主要是两个错误导致的:
- 错误的shell变量写法:
echo $$(file)其实是让shell执行file命令(查看文件类型的工具),而不是输出shell变量$file。在Makefile里传递shell变量的转义逻辑需要根据场景调整。 - 语法错误的重定向:你代码末尾的
" > trial.sh;引号位置完全错误,导致生成的脚本内容混乱。
修正后的代码示例
先给你一个能正常工作的版本,再逐个解释细节:
# 定义可复用的规则模板,注意转义细节 define create_area $(1): $(2) mkdir -p $$(@D); \ cp -nLr -v $(2)/* $$(@D); \ cd $$(@D); \ # 用单引号包裹生成的脚本内容,避免Make展开内部的$变量 echo 'for file in *.v; do echo $file; done' > trial.sh; endef # 遍历A1/A2和B1的组合,生成对应规则 $(foreach a,$(A1) $(A2),\ $(foreach b,$(B1),\ $(eval $(call create_area,$(a),$(b)))))
关键修正点说明
shell变量的正确传递:
- 如果你是直接在Make的shell命令里使用
$file,需要写成$$file(Make会把$$解析成单个$传递给shell)。 - 但这里你是生成另一个shell脚本文件,需要让写入
trial.sh的内容里保留$file,这时用单引号包裹echo的内容最省心——单引号在Make中不会展开内部的$,shell拿到的就是原始的$file。 - 如果你偏好双引号,需要多转一层:
echo "for file in *.v; do echo \$$file; done" > trial.sh;,Make会把\$$处理成$,最终写入trial.sh的是$file。
- 如果你是直接在Make的shell命令里使用
避免call/eval的替代方案:
如果你想完全不用call,可以直接在foreach里拼接规则,虽然可读性差一点,但也能实现:$(foreach a,$(A1) $(A2),\ $(foreach b,$(B1),\ $(eval $(a): $(b))\ $(eval $(a): ; mkdir -p $$(@D); cp -nLr -v $(b)/* $$(@D); cd $$(@D); echo 'for file in *.v; do echo $file; done' > trial.sh;)\ ) )
验证效果
修正后,生成的trial.sh内容会是:
for file in *.v; do echo $file; done
运行这个脚本时,就能正常遍历当前目录下的.v文件并输出文件名了。
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

