【Makefile】Makefile与shell命令为何总是藕断丝连?
博主最近在项目实践过程中,需要深度定制项目的Makefile,其中有些复杂的流程必须得借助shell脚本才能高效地完成,为此博主特意深入学习了在Makefile种调用shell命令的方法。
大家都知道shell命令是Linux的神器,学会灵活应用它可以大大提升执行的自动化效率。
Makefile也是GNU的另一大神器,大家要知道,大名鼎鼎的Linux内核的编译就是靠Makefile来支撑的,连这么庞大、复杂的系统编译它都可以轻松驾驭,可想而知,它也是牛逼哄哄的存在。
我们试想一下,如果把Makefile和shell命令这两大神器联合在一起,会有怎样的效果,我们一起来看下。
一、什么是Makefile?
这个问题,我在之前的博文中就有介绍过,感兴趣的可以点过去看一看:【Linux + Makefile】十分钟教你学会Makefile的FORCE。
下面我再总结下,到底什么是Makefile?
Makefile的基本形式如下所示: TARGET : DEPENDENCES CMD # TARGET:生成的目标,可以是一个文件,也可以是一个虚拟符号(非真实文件) # DEPENDENCES: 生成目标的所有依赖,它是一个集合,可以只有一个文件,或者很多文件;也可以是虚拟符号 # CMD:把所有依赖生成目标的执行命令,其中CMD前面是一个TAB键,而不能是空格 Makefile的基本规则,归纳起来就是一句话:【当一个TARGET (欲生成的目标)比它的任何一个DEPENDENCES(依赖的文件)旧时,这个TARGET就要重新生成】。
这段话读起来很简单,就是Makefile在执行编译的时候,会先去判断TARGET和其依赖文件的时间差,如果TARGET的时间比较旧,意味着依赖文件已经更新了,那么TARGET就要重新生成;反之,如果TARGET的时间比较新,意味着在TARGET生成后,依赖文件是没有改变过的,自然就不用重新去生成TARGET。
说白了,就是搞清楚【TARGET 和 DEPENDENCES 的时间关系】,弄明白了这个,你就可以读懂Makefile了。
二、如何调试Makefile?
以下Makefile给大家演示下如何在Makefile中写注释以及如何调试Makefile的变量。
# This is an annotation for this Makefile.
# Call shell cmd in these ways ...
CUR_PATH = `pwd`
LS_FILES = "`ls`"
LS_TEST_FILES = "$(shell ls test*)"
DEBUG_VARIABLE = "debug"
# Debug your Makefile variables ...
$(info "This is a info msg ...")
$(warning "This is a warning msg ... DEBUG_VARIABLE=$(DEBUG_VARIABLE)")
$(error "This is a error msg ... It will make this Makefile to exist beacuse of an 'error' accur.")
# Begin you targets and dependences
info:
@echo "CUR_PATH = $(CUR_PATH)"
@echo "LS_FILES = $(LS_FILES)"
@echo "LS_TEST_FILES = $(LS_TEST_FILES)"
我们执行下make info可以看下,输出信息:
分别对应info,warning,error;这三个函数是Makefile中内置的,在任意Makefile中都可以使用。但是需要注意的,这三个函数可以在Makefile的任意位置调用,但是必须符合语法规则,比如在CMD部分(见第一章节的引用内容)调用时,必须时TAB开头。
我们试下这个的效果:
三、Makefile定义变量的时候调用shell命令
还是拿上面的Makefile来分析,我们一开始定义了几个变量:
CUR_PATH = `pwd`
LS_FILES = "`ls`"
LS_TEST_FILES = "$(shell ls test*)"
其中CUR_PATH是保存当前Makefile的路径,我们都知道shell命令有个pwd可以获取到当前目录的路径,所以 CUR_PATH 变量正是利用了这个命令来取得当前路径。其中,值得注意的是 ` ` 表示的是获取pwd命令的返回值,这个 ` `,不是分号 ' ',而是键盘左上角数字1左边的那个键值,新手一般非常容易搞错这一点。
第二个变量LS_FILES的定义,使用同样的方式调用了 ls 命令,但是它在返回值的左右,加了 “ ”;这样做的主要目的是,ls的返回值通常是一个字符串,中间每个文件名用空格隔开的;如果我们想把这一系列的字符串连起来而不是分散的,我们就必须使用 " "把它们包起来,这样Makefile变量就会把它当作一个字符串整体来处理。
第三个变量LS_TEST_FILES,它就换了一种方式来调用shell命令,它的格式是 $(shell xxx),其中xxx就是一个可以在shell环境下执行的shell命令,比如本例子中,xxx就是 ls test*。
两者方法没有本质的区别,结合自己的实际情况来使用即可,从易读性来说,推荐使用$(shell xxx)的调用方式。
四、Makefile在执行命令的时候调用shell命令
以下面的Makefile为例子:
# This is an annotation for this Makefile.
shell_cmd:
@ls test*
@pwd
@echo "shell_cmd test ..."
执行make shell_cmd,我们可以看到输出:
只要是shell执行的命令,都可以运行,这就方便我们做一些自动化操作,比如创建目录、删除文件、下载文件、上传文件等等。
五、Makefile以函数的形式调用shell命令
请看下面的Makefile内容
# This is an annotation for this Makefile.
DEBUG_VARIABLE = "debug"
# This is a shell function.
define function_test_shell
param1=$1;\
local_variable=12345678;\
debug_variable=$(DEBUG_VARIABLE);\
echo "param1=$$param1";\
echo "local_variable=$$local_variable";\
echo "debug_variable=$$debug_variable";\
i=0;\
cnt=10;\
for i in `seq $$cnt`; do\
echo "i=$$i";\
done;\
ls -al;\
ls -al test*
endef
# Begin you targets and dependences
shell_function:
@$(call function_test_shell,$(DEBUG_VARIABLE))
我们执行make shell_function可以看到输出:
对应就是shell函数里面的操作。
在Makefile调用shell函数的写法上,有几点特别需要注意: 1)Makefile定义函数是使用define 和 endef,务必配对使用;函数里面的实现可以调用shell命令,也可以调用Makefile内置的函数,也可以调用自定义的Makefile函数; 2)Makefile函数的调用方式是$(call function,xx,yy,zz),其中function是函数名,也就是define后面的名字,xx/yy/zz分别是函数的三个入参;当然有几个参数就填几个,每一个使用 , 隔开; 3) 在函数里面使用1 2 3获取输入的参数,即+数字的方式获取入参; 4)如果函数里面使用的是shell组合命令,请务必在每一条shell命令后面加上 ; 符号,并且每一行加上 \ 连起来;这样对于shell解析器而言,它会认为是一条长长的组合输入命令,而不报语法错误; 5)函数内部使用shell的变量,比如是变量a,正确的写法是$a;而不是常规的shell写法a,也不是Makefile获取变量的方法(a)或{a};这一点特别需要注意。 6)如果函数内部访问的是Makefile定义的变量,比如示例中的访问DEBUG_VARIABLE变量,还是采用$(DEBUG_VARIABLE)的方式。
好了,本期分享就到这里,有疑问的话,欢迎联系我。
相关文章
- 使用adb命令安装apk到手机
- linux命令_linux基本命令的使用
- Linux Shell基础篇三 - 内置命令
- docker 最新安装命令
- linux命令之—-sort命令用于将文本文件内容加以排序详解程序员
- shell神器curl命令的用法 curl用法实例笔记详解程序员
- 脚本Linux下实现精准计时的Shell脚本(linux计时shell)
- 掌握Linux:学习常用Shell命令(linux常用shell命令)
- Shell read命令详解:接收键盘或其它文件描述符的输入
- Shell命令的选项和参数在本质上是什么?
- Linux Link命令——快速链接文件(linuxlink命令)
- 学习Linux:掌握Shell修改技巧(linux修改shell)
- Shell结构命令
- 利用Linux nc命令实现反弹shell漏洞攻击(linuxnc反弹)
- Linux教程:如何开启Shell命令行界面(linux开启shell)
- 命令提升效率:学习Linux的常用Shell命令(linux常用shell)
- linux之美——Shell带来的新体验(linux的 shell)
- 一步搞定:Linux关闭服务器的简单命令(linux关闭服务器命令)
- MySQL无限制访问不再受限于Limit命令(mysql中不limit)
- 空间如何使用DOS命令查看Oracle数据库存储空间(dos查看oracle)
- Shell 命令行操作 Redis简单高效(shell 操作redis)
- 通过Shell脚本管理Redis数据库(shell打开redis)
- 面试中Redis常用命令速查(redis面试命令)
- 解析如何在C语言中调用shell命令的实现方法