2.2.1、shell介绍
- (1)shell可以理解为软件系统提供给用户操作的命令行界面,可以说他是人机交互的一种方式
- (2)我们可以使用shell和操作系统、uboot等软件系统进行交互。具体来说就是我们通过shell给软件系统输入命令,然后回车执行,执行完后会回到shell命令行再次输入命令执行
- (3)我们上述的操作方式一般情况下工作很好,但是有一个缺陷。譬如我们要在linux 下创建一个文件a.c,可以touch a.c,但是如果我现在要在linux下创建100个文件,分别是a1.c a2.c a3.c..............a100.c
如果这时候还是我们去命令行执行命令穿件也可以,但是很累。最好的做法就是把创建过程写成一个shell脚本程序,然后去执行这个shell脚本程序,执行这个程序的效果和手工在命令行输入哪些命令效果一样的。(回忆在arm裸机中安装交叉编译工具链是,创建arm-linux-xxx的符号链接)
2.2.1.2、shell是一类编程语言
- (1)编写shell脚本是使用的语言就是shell语言,又叫做脚本语言
- (2)shell脚本其实是一类语言而不是一个语言
2.2.1.3、常用的shell语言:sh、bash、 csh、ksh、perl、python等
- (1)在linux下常用的脚本语言其实就是bash、sh
- (2)perl、python这样的高级shell脚本语言,常用在网络管理配置领域,系统运维人员一般要学习这些
- (3)脚本语言一般在嵌入式中应用,主要是用来做配置(一个复杂的嵌入式程序都是可配置的,配置过程就是用脚本语言来实现的)自然不会使用过于复杂的脚本语言特性,因此只需要针对性的学习即可
- (4)linux下最常用的脚本就是bash,我们学习也是以bash为主
2.2.1.4、shell脚本的运行机制:解释运行
- (1)C语言(C++)这种编写过程是:编写出源代码(源代码是不能直接运行的)然后编译链接形成二进制程序,然后才能运行了让脚本程序不同,脚本程序编写好后就源代码可直接运行(没有编译链接过程)
- (2)shell程序是解释运行的,所谓解释运行就是说当我们执行一个shell时,shell解析器会逐行的解释shell程序代码,然后一行一行的去运行。(顺序结构)
- (3)CPU实际上只认识二进制代码,根本不认识源代码,脚本程序源代码其实也不是二进制代码,CPU也不认识,也不能直接执行,只不过脚本程序的编译链接过程不是以脚本程序源代码位单位进行的,而是在脚本运行过程中逐行的解释执行时才完成脚本程序源代码转成二进制的过程。(不一定是编译链接,因为这行脚本程序可能早就编译好了,这里我们只是调用它)的。
2.2.2、动手写第一个shell
2.2.2.1编辑器、编译器、运行方法(脚本的3中执行方法)
- (1)shell程序是文本格式的,只要是文本编译器都可以。但是因为我们的shell是要在linux系统下运行的,所以换行符必须是‘\n’,而windows下的换行符是"\r\n",因此windows中的编辑器写的shell不能在linux下运行。所以我们整个课程都是在linux下使用vi编辑器(实际上是vim)进行编写调试的
- (2)编译器: 不涉及,因为shell是解释性语言,直接编辑完就可以运行
- (3)shell程序运行有多种方法,这里介绍三种方法:
第一种:./xx.sh 和运行二进制可执行程序的方法一样,这样运行shell必须要求shell具有可执行权限。chmod a+k xx.sh来添加可执行权限
第二种:source xx.sh source是linux的一个命令,这个命令就是用来执行脚本程序的,这样运行时不需要脚本具有可执行权限
第三种:bash xx.sh bash是一个脚本程序的解释器,本质上是一个可执行程序。这样执行相当于我们执行了bash程序,然后把xx.sh作为argv[1]传给他运行
2.2.2.2、hello world程序的解释
- (1)shell程序的第一行一般都是:#!/bin/sh 这行话以#!开始,后面加上一个pathname,这行话的意思就是指定shell程序执行时被那个解释器解释执行。所以我们在这里写上/bin/sh意思就是这个shell将来被当前机器中/bin目录下的sh可执行程序执行
- 可以将第一行写为;#!/bin/bash来指定使用bash执行该脚本。
- 注意;在 ubuntu上面默认的解释器sh其实不是bash;而是dash.dash是ubuntu中默认使用的脚本解释器
- (2)脚本中的注释使用#, #开头的行是注释行,如果有多行需要注释,每行前面都要就加#(#就相当是C语言//)
- (3)shell程序的正文,由很多行shell语句构成
2.2.2.3、shell并不神秘
- (1)shell就是把一圈命令行中键入执行的命令写成了程序。shell其实就是为了避免反复的在命令行下手工输入而发明的一种把手工输入过程记录下俩,然后通过执行shell脚本程序就能再次复述原来记录的手工输入过程的一种技术
- (2)shell编辑完可以直接运行(不需要编译)
2.2.3、shell编程学习1
2.2.3.1、shell中使用linux命令
- (1)练习1:当前目录下创建文件a.txt
- (2)练习2:当前目录下创建文件夹dir, dir下创建文件b.txt
总结:以上两个练习的目的就是让大家基本学会写脚本,明白脚本编程其实就是把以前在命令行下输入的命令挪到脚本程序中然后一次执行
2.2.3.2、shell中的变量定义和引用
- (1)变量定义和初始化。shell是弱类型语言脚本语言(语言中的变量入股破有明确的类型就属于强类型语言;变量没有明确类型就是弱类型语言),和我们C语言不同。在shell编程中,定义白能量不需要指定类型,在shell中没有类型这个概念,所以在shell中变量可以直接定义。
- (2)变量定义是可以初始化,使用=进行初始化赋值。shell中赋值的两边=两边是不能有空格的。
- 注意:shell对语法非常在意,非常严格。很多地方都是必须没有或者必须有,而且不能随意没有空格
- (3)变量赋值,变量定义后可以再次赋值,新的赋值会覆盖老的赋值。shell中并不可以区分变量的定义和赋值,反正每个变量就是一个符号,这个符号的值就是最后一个给他赋值时的值
- (4)变量引用。shell中引用一个变量必须使用$符号,$符号就是变量解引用符号
注意:$符号后面跟一个字符串,这个字符串就会被当作变量去解析。如果这个自妇女患没有定义,执行时并不会报错,而是吧这个变量解析为空。也就是说shell中没有被定义的变量就相当于是一个定义并赋值为空的 变量
注意:变量引用的时候可以$var,也可以${var}。这两种的区别在某些情况下只能用${var}而不能简单的$var
2.2.3.3、shell中单引号和双引号的区别
- (1)shell中使用字符串可以不加双引号直接使用,而且又空格也是可以的,但是缺陷是不能输出"或者其他转义字符
- (2)shell中也可以使用单引号来表示字符串,也是直接使用的,不能输出转移字符
- (1)单引号中:完全字面转换(不可包含单引号本身)
- (2)双引号中:
$加变量名可以取变量值
反引号仍表示命令替换
\$表示$的字面值 输出$符号
\`表示`的字面值
\"表示"的字面值
\\表示\的字面值
2.2.4、shell 编程学习2
2.2.4.1、shell 中调用linux命令
- (1)直接执行
- (2)反引号括起来执行,有时候我们在shell中调用linux命令是为了得到这个命令的返回值(结果值),这时候就适合用一对反引号(键盘上ESC键下面的那个按键,和~在一个按键上)来调用执行命令。
2.2.4.2、shell中的选择分支结构
- (1)shell的if语言用法很多,再次介绍常用的,其他感兴趣的可以自己去学
- (2)典型的if语言格式
if[表达式];then
xxx
yyy
...
else
xxx
...
fi
- (3)if的典型应用
判断文件是否存在。 (-f) 注意[ ]里面前后都必须要加空格,不能省略。举例:if [ -f a.c ];
判断目录是否存在 (-d)
判断字符串是否相等 ("str1" = "str2"),注意用一个等号而不是两个
判断数字是否相等(-eq)、大于(gt)、小于(lt)、大于等于(ge) 小于等于是(-le) 回忆一下在ARM裸机中讲述ARM汇编条件执行时,曾经就用过这些条件判断的缩写(eq就是equal,gt就是greater than , lt就是less than, ge就是great or equal, le就是less or equal)判断字符串是否为空(-z)
判断字符串是否为空(-z)
注意-z判断是如果变量本身没定义也是不成立的(也就是说-z认为没定义就不等于空)
举例:
#!/bin/shif [12 -eq 12];then echo "yes"
else echo "no"fi
#!/bin/sh
str =""
if [-z $str];then echo "yes" else echo "no" fi
- (4)if 判断中使用“-o”表示逻辑或 。if [ 12 -eq 12 -o "abcd" = "abcd"];then
相当于C语言中在if后面的条件中用裸机与、逻辑或来链接2个式子,最终if中是否成立取决于2个式子的逻辑结果
- (5)逻辑与&&和逻辑或||与简写的if表达式。[ -z $str ] || echo "fei kong"
2.2.5、shell中的循环结构
2.2.5.1、for循环
- (1)要求:能看懂、能改即可。不要求能够完全不参考写出来。因为毕竟嵌入式并不需要完全重新手写shell,系统管理员(服务器运维人员,应用层系统级管理开发的才需要完全掌握shell)
2.2.5.2、while循环
- (1)和C语言的循环在逻辑上无差别
- (2)要注意很多格式要求,譬如:while后面的[ ]两边都有空格,[ ]后面有分号(如果do放在一行的话),i++的写法中有两层括号
2.2.5.3、echo的创建和追加输入文件
- (1)在shell中可以直接使用echo指令新建一些文件,并且将一些内容写到这些文件中去。创建文件并输入内容的关键就是>。
- (2)还可以使用echo指令配合追加符号>>向一个已经存在的文件末尾追加输入内容
2.2.6、shell中的其他值得关注的知识点
2.2.6.1、case语句
- (1)shell中的case语句和C语言中的switch case 语句作用一样,格式有差异
- (2)shell中的case语句天生没有break,也不需要break。和C语言中的switch case butong .shell中的case默认就是匹配上那个就执行那个,不会说执行完了还去执行后面其他的case(就好像shell中的case语言默认都带了break)
2.2.6.2、调用shell程序的传参
- (1)C语言中可以通过main函数的argc和argv给程序传参
- (2)shell程序本身也可以在调用时传参给他。在shell程序内部使用也是使用一些特定符号来表示的。
包括:$#表示调用该shell时传参的个数。($#计数时只考虑真正的参数个数)
$0、$1、$2.....则依次表示传参的各个参数
C语言:./a.out aa bb cc argc = 4, argv[0] =./a.out argv[1] = aa ...............
shell:source a.sh aa bb cc $# = 3 $0是执行这个shell程序的解析程序的名字。 $1表示第一个有效参数的值, $2是第二个有效参数的值........
主要;shell中的很多语法特性和C语言中是相同的,也有很多是不同的。所以大家学的越多越容易混淆(本质原因还是用的不熟悉,用的少),解决方案是:做总结。做笔记,多写代码经常用。
2.2.6.3、while循环和case语言和传参结合
- (1)shell中的break关键字和C语言中意义相同(都是跳出)但是用法不同。因为shell中的break只用于循环跳出。所以当while中内嵌case语句时,case中的break是跳出最外层循环的,不是跳出case语句的。
- (2)shell中的$# $1等内置变量的值不是不可变的,而是可以被改变的额,被shift指令改变。shift指令有点像左移运算符,把我们给shell程序的传参左移了一个一出去了,原来的$2变成了$1,原来的$#少了一个
2.2.7、Makefile基础回顾
2.2.7.1、Makefile的作用和意义
- (1)工程项目中C文件项目太多,管理不方便,因此用Makefile来做项目管理,方便编译链接过程
- (2)uboot和linux kernel本质上都是V语言的项目,都由很多个文件组成,因此都需要通过Makefile来管理。所以要分析uboot必须对Makefile有所了解
2.2.7.2、目标、依赖、命令
- (1)目标就是我们要去make xxx的那个xxx,就是我们最终要生成的额东西
- (2)依赖是用来生成目录的原材料
- (3)命令就是加工方法,所以make xxx的过程其实就是使用命令将依赖加工成目标的过程
2.2.7.3、通配符%和Makefile自动推导(规则)
- (1)%是Makefile中的通配符,代表一个或几个字母。也就是说%.o就代表所有的以.o位结尾的文件
- (2)所谓的自动推导其实就是Makefile的规则。当Makefile需要某一个目标时,他会把这个目标去套规则说明,一旦套衫了某个规则说明,则Makefile会试图寻找这个规则中的依赖,如果能找到则会执行这个规则用依赖生辰目标
2.2.7.4、Makefile中的定义和使用变量
- (1)伪目标意思是这个目标本身不代表一个文件,执行这个目标不是为了得到某个问您或者东西,而是单纯的为了执行下面的这条指令。
- (2)为目标一般都没有依赖,因为执伪目标就是为了执行目标下面的命令。既然一定要执行下面的命令了,那就不必加依赖,因为不加依赖意思就是无条件执行。
- (3)伪目标可以直接写,不应想使用,但是有时候为了明确声明这个目标是个伪目标会在伪目标的前面用 .PHONY来声明它是伪目标
2.2.7.6、Makefile的文件名
- (1)Makefile的文件名合法的一般有2个:Makefile或者makefile
2.2.7.7、Makefile中引用其他Makefile(include指令)
- (1)有时候Makefile总体比较复杂,因此分成好几个Makefile来写。然后在主Makefile中引用其他的,用include指令来引用。引用的效果也是原地展开,和C语言中的头文件包含非常相似
2.2.8、Makefile补充学习1
2.2.8.1、Makefile中的注释用#
- (1)Makefile中注释使用#,和shell一样
2.2.8.2、命令行前面的@用来静默执行
- (1)在makefile的命令行中前面的@表示静默执行
- (2)Makefile中静默情况下再执行一行命令前会先把这行命令给打印出来,然后再执行命令
- (3)如果你不想看到命令本身,指向看到命令执行就静默执行即可
2.2.8.3、Makefile中几种变量赋值运算符
- (1)= 最简单的赋值
- (2):= 一般也是赋值
以上这两个大部分情况下效果是一样的,但是有时候不一样
用 = 赋值的变量,在被解析时他的值取决于最后一次赋值时的值,所以你看变量引用的值不能往前看,应该往后看。
用 := 来赋值的,则是就地直接解析,只用往前看即可。
- (3)?=
如果变量前面并没有赋值过则执行这条赋值,如果前面已经赋值过了则本行被忽略。(实验可以看出:所谓的没有赋值过其实就是这个变量没有被定义过)
- (4)+=
用来给一个已经赋值的变量接续赋值,意思就是把这次的值加到原来的值后面,有点类似于strcat。(在shell makefile等文件中,可以认为所有变量都是字符串,+=就是相当于给字符串strcat接续内容)(注意一个而细节,+=续接的内容和原来的内容之间会自动加一个空格隔开)
注意:Makefile中并不要求赋值运算符两边一定要有空格或者无空格,这一点比shell的格式要求宽松一些。
2.2.8.4、Makefile的环境变量
- (1)Makefile中用export导出到就是环境变量。一般情况下,要求环境变量名用大些,其他的普通变量名用小写。
- (2)环境变量和普通变量不同,可以这样理解:环境变量类似于整个工程中所有Makefile之间可以共享的全局变量,而普通变量知识当前本Makefile中使用的局部变量。所以要注意:定义了一个环境变量会影响到工程中别的Makefile文件,因此要小心
- (3)Makefile中都有一些环境变量可能是Makefile本身自己定义的内部的环境变量(譬如我们在make执行时给makefile传参。 CC=arm-linux-gcc,其实就是贵当前Makefile传了一个环境变量CC,值是arm-linux-gcc.我们在make是给makefile传的环境变量值优先级是最高的,可以覆盖makefile中的赋值)。这就好像C语言中编译器预定义的宏__LINE__ __FUNCTION__等一样。
2.2.9、Makefile补充学习2
-
2.2.9.1、Makefile中使用通配符
- (1)* 若干个任意字符 echo *.c
- (2)? 1个任意字符 echo ?.c
- (3)[ ] 将[ ]中的字符依次去和外面的结合匹配 echo [12].c
还有个%,也是通配符,表示任意多个字符,和*很相似,但是%一般只用于规则描述中,又叫做规则通配符。
2.2.9.1、Makefile的自动变量
- (1)为什么使用自动变量。在有些情况下文件集合中文件非常多,描述的时候很麻烦,所以我们Makefile就用一些特殊的符号来代替符合某种条件的文件集,这就形成了自动变量
- (2)自动变量的含义:预定义的特殊意义的符号
- (3)常见自动变量:
[email protected] 规则的目标文件名
$< 规则的依赖文件名。如果依赖的目标是以模式(即%)定义的,那么“$<”将是符合模式的一系列的文件集。主要,其是一个一个取出来的。
$^ 依赖的文件集合