添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
文章

Makefile和SHELL中$及$$的区别

最近在看linux内核代码时看到在Makefile中用到了 $$() 的使用方式,虽然能猜到什么意思,但不知道使用方法和具体含义,于是查找资料,在此写一个总结

SHELL中的$说明


在shell中, $ 的一种用法是引用shell变量,执行脚本时, $ 引用的变量会被替换为相应的字符串。

当然shell中 $ 的用法远不止于此,此处就不多做展开,想要了解更多,可以阅读 Linux Shell中’$’符号的N种用法

Makefile中的$说明


Makefile中的 $ 用法和shell中的大体类似,只不过在Makefile中, $ 仅能用于引用Makefile声明的变量,无法引用shell的变量。

这里要注意下,使用make命令执行Makefile时并不是shell环境,当执行到Makefile的某个操作时才会执行shell,例:

1
checkstack:
	$(OBJDUMP) -d vmlinux $$(find . -name '*.ko') | \
	$(PERL) $(src)/scripts/checkstack.pl $(CHECKSTACK_ARCH)
kernelrelease:
	@echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
kernelversion:
	@echo $(KERNELVERSION)

注:makefile中对变量的引用需要使用 $() 这种带括号的方式,否则只会识别 $ 后的一个字母

只有执行对应的Makefile命令的shell语句时才会进入shell环境,每行命令独立,每行都是单独的shell,所以上一行定义的shell变量并不适用于下一行。当然如果是使用了 \ 来合并行就可以摆脱这个限制了,比如例子中的 checkstack 命令下的shell命令虽然是两行但在同一个shell环境中执行

Makefile中的$$说明


Makefile命令中的shell语句也并非直接用于shell环境,make会对该语句进行预处理,如果想要引用shell中的变量,就要使用 $ 号来把Makefile变量转换成shell变量

$$ 的用法就是把Makefile引用转化为shell引用,可以理解为此时的 $ 是一个转义符, 也可以理解为去掉一个 $ 后直接带入shell脚本中

例1

1
LIST = one two three
all:
	for i in $(LIST); do \
        echo $i; \

通过make预处理后转化为shell:

1
for i in one two three; do \
        echo ; \
# 输出为空

本例中, $i $(LIST) 会被先当成Makefile变量, LIST 变量在Makefile中有定义,被转换为了 one two three ,由于 i 变量未在Makefile中定义,所以转化为了空。

例2

1
LIST = one two three
all:
	for i in $(LIST); do \
        echo $$i; \

通过make预处理后转化为shell:

1
for i in one two three; do \
        echo $i; \
# 输出为
# one
# two
# three

例2中, $$i 命令被make翻译成了shell命令中的 $i ,此时shell脚本可以正常执行,输出正确结果

例3

1
help:
	@echo  '                    (default: $$(INSTALL_MOD_PATH)/lib/firmware)'

输出结果为:

                    (default: $(INSTALL_MOD_PATH)/lib/firmware)

注:Makefile中的 @ 符号表示该行shell命令不回显,否则执行时make会把转化后的shell脚本打印一遍

注: 单引号 在shell中表示不执行转义或引用,按照原样字符串输出,此处 $(INSTALL_MOD_PATH) 不会被理解为变量

例3中, $$(INSTALL_MOD_PATH) 被翻译成 $(INSTALL_MOD_PATH) ,但由于存在 单引号 ,导致shell变量不会被引用

例4

1
VERSION = 3
PATCHLEVEL = 10
SUBLEVEL = 108
EXTRAVERSION =
# kernel 版本号,为四个版本号的组合
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
# 从shell环境变量中提取shell的执行环境
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
	  else if [ -x /bin/bash ]; then echo /bin/bash; \
	  else echo sh; fi ; fi)
# 选取脚本的目录,如果KBUILD_SRC未定义,则选择$(CURDIR),$(CURDIR)表示当前目录绝对路径
srctree		:= $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
KBUILD_VERBOSE = 1
# 是否在控制台回显,如果有@则不回显
ifeq ($(KBUILD_VERBOSE),1)
  quiet =
  quiet=quiet_
  Q = @
endif
version:
	$(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" > $@

转化后的shell为:

1