GNU Make 4.2.1 如何从空格分隔字符串构建数组?
GNU Make 4.2.1 遍历空格分隔目标列表的实现方案
之前bash方案不生效的原因
找的bash实现方案跑不通,本质是两个常见的认知误区:
- GNU Make 执行 recipe 时,默认调用系统符合 POSIX 规范的 shell(Debian/Ubuntu 下默认是 dash),不会主动启用 bash 的专属语法,依赖 bash 数组的写法会直接抛语法错误
- 没必要执着于把字符串转成“数组”才能遍历——不管是 Make 本身的内置函数,还是 POSIX 标准的 shell,对空白分隔的字符串,天生就支持按单词拆分遍历
方案一:直接在 Make 层面完成遍历(推荐)
你已经拿到的TARGETS本身就是 Make 存储的空白分隔字符串,对于 Make 的内置文本函数来说,这类字符串天生就是可遍历的单词列表,完全不需要转成 shell 侧的数组。
结合你要实现的“检查模式匹配值$*是否为其他目标前缀”的需求,直接用foreach函数就能完成遍历逻辑,示例代码:
# 顶层定义目标列表,比在 recipe 里用 eval 执行 shell 命令效率更高 TARGETS := $(sort $(shell grep -Ph "^[^\t].*::.*##" ./Makefile | cut -d : -f 1)) %:: @echo "当前匹配的模式值: $*" # 直接遍历 TARGETS 中的每个单词,完成前缀判断 $(foreach tgt,$(TARGETS),\ $(if $(filter $(strip $tgt),$(strip $*)%),\ @echo "发现关联目标: $tgt",\ )\ )
这种写法的优势:
- 所有遍历、判断逻辑都在 Make 解析阶段完成,不需要调用额外的 shell 逻辑,执行速度更快
- 完全兼容 GNU Make 4.2.1 的版本特性,不存在不同 shell 环境差异导致的兼容问题
- 不需要处理 shell 转义的问题,避免目标名带特殊字符时触发的命令执行错误
方案二:在 recipe 的 shell 逻辑中实现兼容遍历
如果后续需要在 shell 环境下完成更复杂的处理逻辑,不需要依赖 bash 数组,用 POSIX 全兼容的位置参数拆分法,就能实现和数组完全等价的遍历效果,示例代码:
%:: @set -- $(TARGETS); \ for tgt in "$@"; do \ # 用 shell 字符串截断判断前缀关系 if [ -n "${tgt##$*}" ] && [ "${tgt##$*}" != "$tgt" ]; then \ echo "目标 $tgt 与匹配值 $* 存在前缀关联"; \ fi; \ done
这里的实现逻辑:
set -- $(TARGETS)会把空白分隔的TARGETS字符串,按单词拆分成 shell 的位置参数,存储效果和 bash 数组完全一致- 后续通过
"$@"引用所有拆分后的单个单词,在循环中逐个处理 - 这种写法不需要 bash 环境,不管是 dash、bash、ksh 都能正常执行,从根本上解决了之前 bash 方案不生效的问题
常见避坑点
- 不要在 recipe 中直接使用 bash 专属的数组语法(比如
arr=($VAR)、${arr[@]}),除非你显式指定用 bash 执行命令,否则会直接触发语法错误 - 拆分单词时不要给
$(TARGETS)加全引号,否则整个字符串会被当成单个元素,无法完成逐个遍历 - 前缀判断不要直接做全字符串比对,要使用 Make 的
filter函数或者 shell 的字符串截断逻辑,避免短字符的误匹配
内容的提问来源于stack exchange,提问作者artu-hnrq
相关产品推荐
相关产品推荐

