我在手册中遇到了以下sed
命令。make
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g'
并且有以下效果(这是手册中的一个例子):
main.o : main.c defs.h
# is turned into:
main.o main.d : main.c defs.h
在我看来,'\($*\)\.o'
是为了匹配文件名filename.o
,但我不明白如何'\($*\)'
实现这一点。我知道$
在正则表达式中, 位于行尾是特殊的,但这不适用于此处。
那么'\($*\)'
这里的意思是什么?
完整代码:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
这里使用的是自动 Make 变量
$
的美元符号,而不是表示行尾的符号。$*
在替换的替换部分中,有一个类似的例子
sed
,其中$@
使用了。sed
在执行命令之前,Make 会在表达式中代入这些变量的值sed
。正则表达式中的使用意味着捕获 Make 目标的“词干”(示例中的
\($*\)
字符串),并且可以稍后像在替换文本中一样重新使用它。main
\1
以下
sed
命令会产生相同的效果,但不使用捕获组:重要的是要记住,通常在命令行上给出的正则表达式要
sed
经过两轮解析:一次是通过sh
(这就是为什么你要放在'
引号中),另一轮是通过sed
它自己(这就是为什么你要把它放在\
前面(
以及)
当你想让它们控制分组时)。当你将某些内容放入 Makefile 中时,在它们之前还会有第三轮解析,即
make
。sed
仅当经过前两轮解析后才$
视为行尾。$
在 Makefile 中,
$
指示make
以下文本是需要扩展的变量(或函数);如果下一个符号是(
或{
,则直到下一个匹配的)
或 的所有内容}
都将用作变量或函数名称;否则仅下一个符号将作为变量名称。$(FOO)BAR
扩展到变量的内容FOO
,然后是BAR
;$FOOBAR
扩展到变量的内容F
,然后是OOBAR
;$*
扩展为特殊变量的内容*
,该变量包含当前目标,并且$$
扩展为名为 的特殊变量的内容$
,该变量仅包含文字$
;这样您就可以通过在 Makefile 中$
写入 来将 提供给 shell(第二个解析阶段) 。$$
$$foobar
扩展为单个字符$
,后跟foobar
;,然后shell将其扩展为shell变量的内容foobar
(但前提是变量不是单引号)。默认情况下,Shell 变量和 Makefile 变量是分开的;不要假设相同的名称在两个上下文中都指代相同的值。(的旧版本
make
仅支持$(
...)
而不支持${
...}
,因此使用前者可实现最大的可移植性。)