You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解析Makefile中sed调用的作用及等效实现方式

Makefile中sed命令行的解析与等效改写

首先给出你提供的Makefile代码:

CFLAGS = -O6 -g -Wall
CC     = gcc
VPATH  = ./code

LIB_MOD    = common arith inout log
valid_MOD  = valid $(LIB_MOD)
xporta_MOD = porta four_mot portsort largecalc mp $(LIB_MOD)
ALL_MOD    = $(valid_MOD) $(xporta_MOD)

all: xporta valid

valid: $(valid_MOD:%=%.d) $(valid_MOD:%=%.o) 
    $(CC) -o bin/$@ $(valid_MOD:%=%.o)

xporta: $(xporta_MOD:%=%.d) $(xporta_MOD:%=%.o) 
    $(CC) -o bin/$@ $(xporta_MOD:%=%.o)

clean:
    rm -f *.o bin/xporta bin/valid

%.d: %.c
    $(SHELL) -ec 'gcc -MM $(CPPFLAGS) $< \
        | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
        [ -s $@ ] || rm -f $@'

include $(ALL_MOD:%=%.d)

问题聚焦

你无法理解以下代码段的作用、展开结果及等效写法:

| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \ [ -s $@ ] || rm -f $@'

一、命令行的展开结果

先明确Makefile自动变量的含义:

  • $*:匹配%.d模式中的%部分,比如处理foo.d时,$*就是foo
  • $@:当前目标文件名,比如处理foo.d时,$@就是foo.d
  • $<:当前依赖文件,即对应的foo.c

当处理foo.c生成foo.d时,这段命令展开后为:

gcc -MM $(CPPFLAGS) foo.c | sed 's/\(foo\)\.o[ :]*/foo.o foo.d : /g' > foo.d; [ -s foo.d ] || rm -f foo.d

具体作用拆解

  1. gcc -MM foo.c:生成foo.o的头文件依赖,输出格式类似foo.o: foo.h bar.h utils.h
  2. sed 's/\(foo\)\.o[ :]*/foo.o foo.d : /g':把输出中的foo.o:替换成foo.o foo.d :,最终输出变为foo.o foo.d : foo.h bar.h utils.h——这样当头文件更新时,不仅会重新编译foo.o,还会重新生成foo.d文件
  3. > foo.d:将处理后的内容写入foo.d文件
  4. [ -s foo.d ] || rm -f foo.d:检查foo.d是否为非空文件,若为空(比如编译出错时)则删除它,避免后续include空文件导致Make报错

二、不使用sed的等效写法

方式1:用awk替代sed(兼容旧环境)

awk可实现相同的文本替换逻辑,写法更直观:

%.d: %.c
    $(SHELL) -ec 'gcc -MM $(CPPFLAGS) $< \
        | awk -v target="$@" "{sub(/^[^:]+:/, \"& \"target\" \"); print}" > $@; \
        [ -s $@ ] || rm -f $@'

这段awk命令会把每行开头的xxx.o:替换成xxx.o xxx.d :,和原sed效果完全一致。

方式2:使用gcc内置参数简化(现代易读写法)

如果你的环境支持较新版本的gcc(兼容-MMD和-MP参数),可以完全抛弃手动生成.d文件的复杂逻辑,改用更简洁的写法:

CFLAGS = -O6 -g -Wall
CC     = gcc
VPATH  = ./code

LIB_MOD    = common arith inout log
valid_MOD  = valid $(LIB_MOD)
xporta_MOD = porta four_mot portsort largecalc mp $(LIB_MOD)
ALL_MOD    = $(valid_MOD) $(xporta_MOD)

all: xporta valid

# 用-MMD自动生成.d文件,-MP避免头文件删除时报错
%.o: %.c
    $(CC) -c $(CFLAGS) $(CPPFLAGS) -MMD -MP $< -o $@

valid: $(valid_MOD:%=%.o) 
    $(CC) -o bin/$@ $(valid_MOD:%=%.o)

xporta: $(xporta_MOD:%=%.o) 
    $(CC) -o bin/$@ $(xporta_MOD:%=%.o)

clean:
    rm -f *.o *.d bin/xporta bin/valid

include $(ALL_MOD:%=%.d)
  • -MMD:自动生成.d文件,包含*.o的头文件依赖
  • -MP:自动为每个头文件生成空的phony目标(比如foo.h:),避免头文件被删除时Make报错
  • 这种写法去掉了所有sed/awk调用,还简化了目标依赖(无需显式依赖.d文件),可读性大幅提升

内容的提问来源于stack exchange,提问作者One_Cable5781

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:50:55