GNU Make v4.4.1多行变量短横线莫名剔除问题咨询
GNU Make多行变量短横线被剔除的问题与解答
问题背景
我正尝试使用GNU Make v4.4.1定义用于YAML文件头的多行变量,示例内容如下:
#foo ---
需求是在Recipe中将该头内容输出到文件,但短横线在部分场景下会被莫名剔除。
最小可复现示例(MWE)
.ONESHELL: define dashes # dashes below stripped off --- endef define dashes_hex # hex dashes below kept \x2d\x2d\x2d endef define only_dashes --- endef define dashes_first --- # dashes above kept endef .PHONY: a b c d a: @echo -e "a>$(dashes)<" b: @echo -e "b>$(dashes_hex)<" c: @echo -e "c>$(only_dashes)<" d: @echo -e "d>$(dashes_first)<"
测试结果
$ make a b c d a># dashes below stripped off < b># hex dashes below kept ---< c>---< d>--- # dashes above kept<
观察结果可知:
- 场景b:使用十六进制值替代短横线可作为临时解决方案;
- 场景c:单行纯短横线可正常输出;
- 场景d:首行的纯短横线可正常输出。
核心疑问:GNU Make中纯短横线有何特殊处理逻辑?为何场景c和d的结果正常?
问题解答
核心原因:Make的Recipe行预处理规则
GNU Make解析Recipe时会执行以下步骤:
- 先展开所有变量;
- 将展开后的内容按换行分割为多行;
- 对每一行进行预处理:
- 如果行首以短横线
-开头,Make会自动剥离这些短横线(原本用于标记「允许命令执行失败,不终止流程」); - 如果行首以
#开头,会被视为注释行直接跳过。
- 如果行首以短横线
在场景a中,$(dashes)展开后是两行内容,导致Recipe的echo命令被拆成两行:
echo -e "a># dashes below stripped off ---<"
Make将其识别为两个独立的Recipe行:
- 第一行:
echo -e "a># dashes below stripped off - 第二行:
---<"
对第二行,Make剥离了行首的所有短横线,得到<"。加上.ONESHELL让两行在同一个shell实例执行,第一行的echo因引号未闭合,shell会把第二行的<"作为引号结束,最终执行的是echo -e "a># dashes below stripped off\n<",所以原本的---被完全剔除。
场景c、d正常的原因
- 场景c:变量是单行的
---,展开后Recipe只有一行:echo -e "c>---<",Make不会拆分,直接传递给shell执行,未触发行首短横线的剥离规则,因此内容正常输出。 - 场景d:变量展开后是两行,Recipe被拆分为:
第二行以echo -e "d>--- # dashes above kept<"#开头,Make将其视为注释行,但.ONESHELL让shell把这行内容作为第一行echo的引号闭合部分,最终执行的是echo -e "d>---\n# dashes above kept<"。而第一行里的---是echo字符串的一部分,并非独立的Recipe行首,所以不会被Make剥离,内容完整保留。
替代解决方案(除十六进制转义)
- 用
printf替代echo:避免换行带来的解析问题a: @printf "a>%s<" "$(dashes)" - 转义变量内的换行:确保变量展开后是单行
define dashes # dashes below stripped off \ --- endef - 替换短横线为转义字符:提前规避Make的识别逻辑
a: @echo -e "a>$(subst -,\\x2d,$(dashes))<"
内容的提问来源于stack exchange,提问作者sphakka
相关产品推荐
相关产品推荐

