高级C总结

知识点储备

1、gcc的编译流程

第一步:预处理—》将C源文件编译成C文件
头文件展开,注释去掉,宏替换掉,不做语法检查
gcc -E test.c -o test.i
第二步:编译–》将预处理之后C文件编译成汇编文件
gcc -S test.i -o test.s
第三步:汇编—》将汇编文件编译生成机器文件
gcc -c test.s -o test.o
第四步:链接—》将所有的机器文件共同参与链接,生成一个可执行文件
gcc *.o -o App

2、对于传参时,到底传普通变量,还是传一级指针变量,又或者传二级指针甚至多级指针的选择思路?

—》看是否需要在子函数内部引起实参本身发生改变。
eg:
int a = 90,b = 89;
子函数中实现ab两个数字的交换—》(在子函数中引起实参a,b本身发生改变,因此需要将a,b的地址传过去)
func(&a, &b);—>才可以在子函数中通过指针来间接引起实参a,b的交换

int *p = NULL;
子函数中实现给实参p赋值一个连续空间的首地址(子函数中引起实参p此时会具备有一个首地址,因此需要将&p传过去)
func(&p); —> Pointer = &p; -->Pointer = p;通过给Pointer赋值就相当于给实参p赋了值。

3、main函数传参

main函数无参:
int main()
{

}

int main(void)
{

}
main带参数:
int main(int argc, const chat *argv[])
{

}
解释:
int argc:代表传递给main 函数的参数的个数,系统自己会计算传递过来的参数个数
const char *argv[]:代表存储传递给main函数的所有参数的地方

4、二维数组的图解

在这里插入图片描述

LinuxC

理解:Linux系统 C高级(函数和指针的结合,动态内存管理,结构体,共用体,大小端,多文件编译,Make工程管理, 递归函数,回调函数,枚举,GDB调试工具)

1、C高级

1、指针函数

概念:函数返回值类型是指针类型的函数

2、函数指针

2-1:函数指针概念
概念:指向函数的指针(函数的本质是存在内存中的一段二进制代码,因此会占据一片空间,同时该片空间的首地址用函数名来代替)

思考:
如何定义一个指向整形的指针?
—》int *p
如何定义一个指向含有5个int类型元素的一维数组?(数组指针)
—》int (*pArr)[5];
2-2:函数指针的定义格式
定义函数指针方式:
返回值类型 (*pFunc)(形参列表);

思考:
void func(int arr[20]);//这里的[]就是*号,只是允许用户可以这样去写,所以等价于这里写的arr就是一个指针变量。
void func(int *arr)
sizeof(arr) = 8 ;
2-3:关键字typedef取别名的用法
typedef:给已存在的一个数据类型在其作用域内取一个别名
用法:
typedef 类型名 新类型名;
注意:对于基本类型可以按照以上取别名的方式去写,但是对于函数指针类型需要用以下的的方式进行定义。
eg:
typedef int MYInt;
MyInt a;
注意:
针对函数指针类型取别名的写法:
返回值类型 (*新类型名)(形参列表);
2-4:函数指针数组的定义
函数指针数组:数组元素类型是函数指针类型的数组。
定义:
函数指针类型 数组名[元素个数]; //取完别名之后可以这样定义函数指针数组。
返回值类型 (*数组名[元素个数])(参数表列表);

2-5:回调函数
概念:把B函数的地址即(函数名或者&函数名)作为A函数的参数,传递给A函数,在A函数中通过使用这个参数来间接调B函数的过程就称为“回调函数”

3、动态内存管理

针对:堆区
在堆区中:常见的操作有—》申请空间,释放空间,初始化空间
因此:以上操作全局需要借助于函数来实现:
申请空间:malloc calloc
释放空间:free
初始化空间:memset
扩容空间:realloc
头文件:
#include <stdlib.h>
函数原型:
3-1:申请空间
1、void *malloc(size_t size);
功能:申请空间在堆区中
参数:所需申请空间占据的字节数
返回值:成功返回连续空间的首地址,失败返回NULL
2、void *calloc(size_t nmemb, size_t size);
功能:申请空间
参数:
参数1:所需申请的元素个数
参数2:元素的大小(字节数)
返回值:成功返回连续空间的首地址,失败返回NULL
注意:calloc在申请完空间之后,会自动将申请的连续空间初始化为’\0’

3-2:释放空间
3、void free(void *ptr);
功能:释放空间
参数:所需释放空间的首地址
返回值:无返回值
3-3:扩容空间
4、void *realloc(void *ptr, size_t size);
功能:用来扩容空间
参数:
参数1:所需扩容空间的首地址(malloc的返回值)
参数2:扩容之后总的字节数(旧 + 新)
返回值:成功返回扩容之后空间的首地址,失败返回NULL
注意:扩容之后的空间首地址可能和之前的地址一样,也可不一样!!!

  #include <string.h>

3-4:重置空间
5、void *memset(void *s, int c, size_t n);
功能:用来重置一片内存空间 (当做清空空间居多!)
参数:
参数1:所需重置内存空间首地址
参数2:所需重置的字符 —》’\0’ , 0
参数3:所需重置内存空间的大小—》sizeof()

练习:
使用malloc申请可以存储5个Int元素的空间,要求:malloc , input, output,sort,realloc,编写成子函数,最终释放空间。

4、结构体

概念:是一种构造数据类型,目的是为了将一个事物的多重属性表示清楚(属性可能是不同的数据类型,也可以是相同的数据类型)。

4-1:如何构造一个结构体类型?
struct 结构体名
{
数据类型1 成员1 ;
数据类型2 成员2;
。。。。
数据类型N 成员N;
};
4-2:如何定义结构体变量?
struct 结构体名 变量名;

4-3:如何给结构体成员进行赋值?
变量名= {值1,值2.。。。。。。};
struct 结构体名 变量名 = {值1,值2,。。。。。};

4-4:如何访问结构体中的成员?
方式1:变量名.成员名;
方式2:结构体指针->成员名;

定义结构体数组,实现对于一批数据的输入输出。

4-5:结构体的字节对齐
概念:
在申请结构体空间时,会遵循“字节对齐”的原则!
什么是字节对齐?
—》分类讨论
第一点:考虑在范围之内的成员必须为基本数据类型!
在32OS:系统最多一次性开辟4个字节
怎样确定每一次开辟空间的大小,依赖于成员中占据字节空间最大的那个数据类型,将其作为基准,来进行每一次空间大小的开辟,但是成员有超过上限4字节的数据类型,此时仍按照4字节进行开辟空间。反之,则按照那个最大的类型占据的字节数进行开辟。
在64OS:系统最多一次性开辟8个字节
怎样确定每一次开辟空间的大小,依赖于成员中占据字节空间最大的那个数据类型,将其作为基准,来进行每一次空间大小的开辟,但是成员有超过上限8字节的数据类型,此时仍按照8字节进行开辟空间。反之,则按照那个最大的类型占据的字节数进行开辟。
第二点:
注意:
不管是32OS还是64OS,在存储成员时,均需要保证存储的地址编号和该成员自身的数据类型大小成整数倍的关系,否则往后寻找下一个合适的地址空间进行存储。

因此结合以上两点累确定结构体类型占据的字节数!!!。

5、共用体

概念:多个成员共用同一片空间,因此开辟内存空间时,会按照占据内存空间最大的数据类型进行开辟。

定义格式:
union 共用体名
{
数据类型1 成员1;
。。。。
数据类型N 成员N
};

注意:共用体的使用方法和结构体一致,唯一区别是:结构体的成员空间是独立的,而共用体的成员共用同一片空间。

6、枚举

概念:跟宏比较类似,意味着也是一些常量值,只不过这些常量值是被放在一个集合里面。

如何定义一个枚举类型?
enum 枚举名
{
枚举数值名1,
枚举数值名2,
。。。。
};

7、大小端(字节序)

概念:计算机中存储数据的时候到底将低字节或者高字节处的内容存储在低地址还是高地址的问题。
大端存储:低字节处的内容存储在高地址处,高字节处的内容存储在低地址处。
小端存储:低字节处的内容存储在低地址处,高字节处的内容存储在高地址处。

低字节:一串二进制数据的右边
低地址:小的地址编号
在这里插入图片描述
代码:共用体验证大小端存储的问题
在这里插入图片描述

8、调试BUG的方式

方式1:printf打印 —>最直接
在自己感觉出错的代码段前后加上部分printf输出内容,然后编译运行来判断问题的出处。

方式2:gdb调试工具
流程:
(1)生成带有调试信息环境的可执行文件
方式:gcc -g test.c -o App
(2)进入该可执行文件
方式:gdb App
(3)设置断点 -->让程序从哪里开始调试
方式:b 行标/函数名
(4)运行
方式: r
(5)单步运行
n: 不进入子函数内部,但不代表不执行子函数
s: 进入子函数内容,并开始执行子函数中每一行代码
(6)找到错误所在的行
响应:看到程序打印出一行文本“Program recived Sigmentation fault…”,上一行就是引起错误的所在行。
(7)结束调试
方式:按下q退出即可。

9、对于段错误的理解

在C语言阶段:出现段错误的原因:
(1)数组越界
(2)操作空指针或者野指针
(3)修改常量区内容

10、递归函数

概念:重复调用其他函数或者调用自己,但是调用的时候一定要注意结束调用的条件。

如何编写一个标准的递归函数?
三要素:
(1)从哪里开始?
(2)从哪里结束?
(3)每一步干什么?

阶乘:
5! = 120 (5 * 4 * 3 * 2 * 1 = 120)
在这里插入图片描述

在这里插入图片描述
练习:使用递归函数完成斐波那契数列的打印
1 1 2 3 5 8 13 21.。。。。。。。
实现要求:输入需要打印的个数N,就输出对应的前N项。

11、头文件的书写(多文件编译)

11、头文件的书写(多文件编译)
格式:条件编译—》防止同一个头文件被重复定义
创建一个后缀是.h文件eg: touch test.h
打开test.h —> vim test.h
编写以下代码:
#ifndef TEST_H
#define TEST_H


1、宏定义
2、结构体定义
3、取别名
4、全局变量的声明
5、函数的声明
6、枚举类型的定义

#endif

之前:书写代码需要一个C文件即可
现在:
具备书写一个项目的思想
项目是什么样的?
—》会有很对不一样的需求,因此每一个需求可能会交给一个人去完成,因此书写一个完整的项目需要多个人去负责,意味着会有多个.c文件需要进行书写,也需要有一个.h文件作为辅助,进行编译。
注意:对于以上需求,需要通过主文件进行调用—》.c文件
综上:
1个主文件 + 若干个子文件 + 若干个头文件

12、Make

概念:工程管理工具
可以将很多.c文件+.h文件进行统一管理

使用:
命令行输入make 目标名 即可
如何编写一个Makefile文件?
—》统一格式
目标:依赖
[Tab]命令表

嵌套Makefile:
在这里插入图片描述

12-1:Make中的一些变量
(1)用户自定义变量
格式:
变量名:=值 (定义变量且赋初值,变量名建议大写)
(2)自动变量
$< :依赖中的第一个依赖
$^ :所有依赖
$@:目标名
(3)系统预定义变量
CC:编译器的名称,默认值是cc ,用户可以自己赋值为: eg CC :=gcc
CFLAGS:编译器的选项,无默认值,用户可以自己赋值:eg: CFLAGS:= -c -g -Wall
RM:代表删除,默认值为:rm -f,用户可以自己赋值为 eg: RM := -rf
(4)环境变量
export :添加一些指定的环境变量到整个系统环境中去
eg:
export 变量1 变量2 变量3

普通版和进阶版对比:因为引入了变量。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

作业:

1、将一个带有空格的字符串进行拆分,把拆分之后(不含空格)的字符串放在指针数组中,最后打印指针数组的内容查看拆分的结果
eg:
char str[100] = {“ wang jia jia ”};
char *pStr[N] = {“wang”,”jia”,”jia”};
注意:以上代码的书写需要按照嵌套Makefile的格式书写。

LinuxOS

1、Linux的发展史

1970年:UNIX 采用汇编语言编写(不能移植) AT&T && MIT
1973年:C语言—》重写
1979年:—》收费
1983年:RMS —》GNU计划—》开发出一套兼容UNIX的类UNIX系统(操作系统:GUN/Hurd),
自由软件基金会协助该计划的实施,让律师起草一个GPL的协议。
1989年:编写完毕GNU/Hurd操作系统相关的软件:gcc make… 但是Hurd未实现。
1991年:Linus —》 编写磁盘驱动程序+文件系统,形成内核雏形,并通过GPL协议加入GNU计划,最终系统更名为:GNU/Linux。

2、Linux的发行版本

商业组织维护的发行版本:
RHEL :(RedHat EnterPrice Linux),收费且稳定,适合作为研发搭建的大型服务器
Fedora:社区版的Redhat发展而来,免费,稳定性稍差,适合作为桌面服务器
CentOS:RHEL的克隆版本,免费,稳定性稍差,适合作为桌面服务器
社区组织维护的发型版本:
Debian:社区类Linux系统的典范,最遵循GNU计划(通过GPL协议)—》特点:包管理方式(软件包的安装以及压缩打包。。。。。)
Ubuntu:继承于Debian,又添加自己独有的一些机制(通配符,上下键翻阅历史命令以及操作,Tab自动补全。。。。)

3、Linux的体系结构

内核:核心组件,负责管理操作系统的执行。
进程管理,内存管理,网络管理,文件系统管理,设备驱动管理
在这里插入图片描述

shell:本质是一个命令行解释器,起到中间桥梁的作用,负责接收用户输入的命令,并简化其翻译提交给内核,让内核做出回应。
在这里插入图片描述
在这里插入图片描述

文件系统:
数据在设备或者分区上组织文件的一种方法(数据结构)。
分为:
磁盘文件系统(U盘),虚拟文件系统(隐藏了跨文件系统类型数据传输的细节,调用者只需要使用统一的接口,即可完成不同文件系统类型的之间的数据交互。),网络文件系统(进程间网络通信时,数据的接收以及转发)。

实用程序:每一个独立的系统,都会配备自己专属的一套程序:编辑器,编译器,浏览器,办公套件,数据库。。。。

4、Linux的软件包管理

软件包的命名方式图解:
在这里插入图片描述

目的:学会下载软件
方式1:离线安装
没有网络,首先本地必须存在待于安装的包文件,其次软件包之间的依赖关系需要自己建立,所以不建议使用(特殊情况)
在这里插入图片描述

方式2:在线安装
—》条件:有网络
检测Ubuntu系统是否有网络?
—》ping www.baidu.com
如何检查虚拟机的IP地址?
—》ifconfig

工具:APT
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

注意:
在这里插入图片描述

代码关联文件的安装:
在这里插入图片描述

思路:
1、在工程总目录下,创建tags文件,目的是将工程中所有的文件进行关联
代码为:命令行输入–》ctags -R (-R代表递归关联)
2、在含有tags文件的路径下打开一个文件,比如:vim ./include/stu.h
3、在需要跳转的位置,比如经常在函数声明的位置,输入 ctrl }这个组合键,即可跳转至该函数的实现文件处
4、阅读完毕代码之后,通过ctrl t 组合键实现返回上一次跳转的位置。

如何设置一个固定的IP地址?
关心4个参数:IP地址(192.168.0.num) , 子网掩码(255.255.255.0) , 网关(192.168.0.1), DNS服务器(8.8.8.8 / 8.8.4.4)

5、Linux的网络基础

1、IP地址:

IP:Internet Protocol ---->网络协议,在通信中起到路由的功能
IP地址的形式:由点分十进制的数字组合而成,总共占4给字节(32位二进制数据):eg:192.168.0.89
IP地址的组成:网络地址(高位) + 主机地址(低位)
IP地址类型:因为网络地址和主机地址所占字节数的不同,可以划分为:A类 B类 C类 D类 E类
IP地址5种类型的划分,按照网络地址和主机地址所占的比例:
A类IP:网络地址占1个字节,主机地址占3个字节,并且规定网络地址的最高位是0
范围:0.0.0.0 ~ 127.255.255.255
注意:
0.0.0.0为任意地址,用户不会获得,网络地址会从1开始,主机地址也是从1开始分配给用户,127开头的IP地址都是本地环回地址,用户无网络时进程间通信,以255结尾的IP地址都是广播地址,用户也不会被分配到,因此,用户在A类IP范围中,可以获取的有效IP地址范围为:1.0.0.1 ~ 126.255.255.254

   B类IP:网络地址占2个字节,主机地址占2个字节,并且规定网络地址的最高位是10
   C类IP:网络地址占3个字节,主机地址占1个字节,并且规定网络地址的最高位是110
   范围:192.0.0.1 ~ 223.255.255.254 (优化之后的有效IP地址范围)
   D类IP:目前用于组播实现比较多
   E类IP:目前处于保留,等待将来使用

2、子网掩码:

作用:判断当前两台主机之间的通信是外网通信还是内网通信
方法:
A:192.168.0.199 —》子网掩码:255.255.255.0
B:192.168.2.188 —》子网掩码:255.255.0.0
假如A想给B发消息:把A和B的IP地址均和A的子网掩码进行相与,查看网段是否一致,如果一致则代表可以直接通信即内网通信,否则需要外网通信(借助网关)。
假如B想给发消息:把A和B的IP地址均和B的子网掩码进行相与,查看网段是否一致,如果一致则代表可以直接通信即内网通信,否则需要外网通信(借助网关)。

3、网关:

作用:由一台主机通往另一台主机的IP地址(针对外网通信的时候)
网关可以有多个,但是一般会有一个默认网关:C类–》192.168.X.1 (X是网段号)

4、DNS服务器:

域名解析服务器,存储的是域名和IP的对应关系,当用户访问域名的时候,就会查询DNS服务器上的表,进而找到域名对应的IP地址。
eg:
ping www.baidu.com —>最终还是借助于DNS服务器:8.8.8.8 / 8.8.4.4 来查找www.baidu.com对应的IP地址
如果没有DNS服务器,就不识别域名,会报Unknown host。。。。

6、Linux下的shell命令

命令的学习:三要素
命令名称 [选项] [参数];
总结:
1、命令名称不能省略
2、选项和参数是可选的(可有可无可多个)
3、不管是命令,还是代码在同一行写不完时,需要先输入空格,在输入反斜杠,再按下回车即可在下一行进行书写。
4、将多个命令书写在一起时,多个命令之间需要通过分号来隔开。

1、用户切换命令

su:切换用户状态的命令
方式:
方式1:su 用户名:将用户身份切换至指定的用户状态,此时所在路径和切换之前的用户位置一样。
方式2:su - 用户名:将用户身份切换至指定的用户状态的同时,也将用户之前所在的路径切换至指定用户的家目录。

2、用户管理命令

1、修改密码

格式:
sudo passwd 用户名
注意:
输入:passwd也可修改密码,但是默认修改当前用户的密码(密码只能越来越长!,所以不建议,还是记住sudo passwd 用户名这个通用的公式即可。)

2、创建新用户

格式:
sudo adduser 新用户名

3、删除用户

格式:
sudo deluser 用户名
注意:想要彻底删除用户的时候,添加–remove-home这个选项即可。

3、常见的shell命令

1、env

打印环境变量以及变量对应值的命令,环境变量的值为多个时,以冒号隔开。

2、echo

标准输出命令,功能类似于Printf
用法:
方式1:打印指定的一个字符串
方式2:打印变量的值(注意:通过$符号完成对变量值的获取)
总结:不管是方式1还是方式2,通过echo 输出的时候,均需要书写””引起来就不会出错。

3、ln

创建链接文件的命令
链接文件的分类:
软链接:称为符号链接文件,以源文件的所在路径创建的一个文件,意味着软链接文件存储的是指向源文件的一条路径,类似于Windows下的快捷方式。
创建方式:
ln -s 源文件(带绝对路径) 软链接文件

硬链接:以物理编号inode编号创建的文件,意味着:
方式:
ln 源文件(相对/绝对路径) 硬链接文件
注意:源文件的写法,可以不加绝对路径,因为和路径没关系。只是源文件的一个别名。

对于软链接和硬链接文件的总结:
软链接:
1、源文件和软链接是两块空间,并且软链接文件存储的是指向源文件的一条路径
2、源文件和软链接的影响是双向的。
3、创建软链接文件时,源文件建议写成绝对路径的方式,可以提高软链接文件的移植性。
4、当删除源文件时,软链接立马失效,当恢复(创建一个与之前源文件同名的新文件)源文件时,此时软链接文件会生效,但是此时指向的是最新的文件。
硬链接:
1、源文件和硬链接是同一片空间,可以理解为硬链接是源文件那片空间的一个别名而已。
2、源文件和硬链接的影响是双向的。
3、创建硬链接文件时,源文件以相对路径或者绝对路径都可以,不依赖路径而存在。
4、当删除源文件时,硬链接不受影响,可以理解为那片空间的名字少了一个,此时依旧可以通过硬链接来打开之前源文件的空间并进行操作。(可以当做备份或者误删来使用)。
注意:如何确保两个文件是否为同一个文件?
-----》打印分配给每一个文件物理编号inode号来查看—》ls -i 即可打印出编号。

4、chmod

对于文件的文权限进行更改
创建新文件的初始权限是:rw- rw- r-- —》0664,如果需要对该文件的 权限进行升级或者降级时,就需要通过chmod来做。

假设:文件为:test.c —>初始权限:0664
方式1: 八进制法
chmod 0774 test.c / chmod 774 test.c

方式2:符号标记法
用户:u
用户组:g
其他用户:o
所有用户:a
chmod u+x test.c
chmod u-x test.c
chmod a+x test.c
chmod +x test.c (等价于a + x)

5、tar

来实现打包相关的命令
选项:
-c:打包
-x:解包
-v:可视化(可以看到打包,压缩,解包,解压的过程)
-z:可以调用压缩工具为gzip
-j:可以调用压缩工具为bzip2
-f:可以指明需要打包压缩的文件名
注意: -C 代表可以指定解包或者解压之后的位置,如果不写,则默认解包或者解压到当前用户工作位置。
在这里插入图片描述

6、grep

grep 选项 格式 路径

解释:
选项:
-c:匹配包含指定格式所在行的行数
-n:匹配包含指定格式所在行
-v:匹配除了包含指定格式所在行的其他行

7、find

查找,查找对象是文件
格式:
find 路径 选项 描述
解释:
-name:代表需要查找指定文件名

8、wc

统计文件的大小,单词数,以及文件的行数
在这里插入图片描述

4、shell特殊字符

1、通配符
  • :匹配任意长度的内容。—》最为常用。
    ?:匹配含有一个长度的内容
    [范围]:匹配含有范围之内的一个长度的内容
    [指定范围]:匹配含有指定范围之内的一个长度的内容
    [^指定范围]:匹配除了指定范围之外的任何一个长度的内容
2、管道

格式:
command1 | command2 | … | commandN
理解:
将前一个命令的输出作为后一个命令的输入
在这里插入图片描述

补充:xargs 命令—》会具备转换功能(将输出格式化为一行,作为参数赋值给下一个命令)

3、命令置换

格式:
Command1 Command2
理解:将Command2的输出作为Command1命令的参数
思考:管道和命令置换有啥区别?
—》理解:
管道是将输出作为输入,等价于将输出作为文本流,所以接下来的操作就会直接对该文本流进行操作
命令置换将输出作为参数,等价于在接下来操作中会进入参数所代表的文件里面去操作。

4、重定向

概念:重新定义输出或者输入的方向为自己指定的
输入重定向:之前对于输入,默认的方向为键盘,现在可以将默认的输入方向即从键盘获取输入源改变到从比如文件中去获取一个输入源。
eg: cat:查看指定文件的内容
cat 1.c —》 cat < 1.c ( <可以省略不写 )
输出重定向:之前对于输出,默认的方向为终端,现在可以将默认的输出方向从向终端输出改变到向比如一个文件中去输出。
情况讨论:

:重定向到的文件不存在时,新建并打开并存储,反之则直接先清空之前内容,再打开并存储,但是只能接收正确命令的结果。

:重定向到的文件不存在时,新建并打开并存储,反之以追加模式打开文件并存储,但是只能接收正确命令的结果。

2>:
重定向到的文件不存在时,新建并打开并存储,反之则直接先清空之前内容,再打开并存储,但是只能接收错误命令的结果。

2>>:
重定向到的文件不存在时,新建并打开并存储,反之以追加模式打开文件并存储,但是只能接收错误命令的结果。
&>:
重定向到的文件不存在时,新建并打开并存储,反之先清空之前内容,再打开并存储,不论正确输出或者错误的结果均可接收并存储。

&>>:
重定向到的文件不存在时,新建并打开并存储,反之以追加模式打开文件并存储,不论正确输出或者错误的结果均可接收并存储。

Linux下的shell编程

1、脚本的理解

脚本也是由一系列命令组合起来,并且加入了一定的控制结构之后所形成的文本,就称之为脚本,优点是可以自动且重复的去执行某件事情,比如:开机或者关机之前以及之后可以自动的去执行一部分代码,因为脚本也是多条shell命令的有序结合,因此在终端上能够完成的事情全部可以使用脚本来完成(重复使用)。
当执行脚本的时候,shell这个解释器就会逐行去翻译命令,并将结果提交给内核,内核接收到结果之后,就会去驱动硬件进行响应。

因此:脚本就是一门解释型的语言,本质是多条shell命令的有序集合(可能会加入一定量的控制结构语句)。

总结:
shell命令:理解为用户发送给内核一种信号(该信号是由shell来完成解释)
shell:命令行解释器(专门负责将用户输入的命令或者语句翻译给内核)
shell脚本:多条shell命令的有序集合(可能会引入一些控制结构语句)

2、脚本的编写格式

第一步:顶格书写:#!/bin/bash ----》交代即将书写完毕的脚本由谁完成解释工作。
第二步:按照shell规定的语法进行编码即可。

3、脚本的编程步骤

第一步:新建一个后缀为.sh的文件(和之前C文件一样)
第二步:修改文件的权限(chmod 来修改文件的权限为可执行,至少自己具备可执行权限)
第三步:直接执行(./可执行文件名字 运行即可)

4、脚本的语法

对于变量的定义:脚本中的数据类型称为“弱类型”,意味着没有数据类型的区分。
4-1:自定义变量
方法:
变量名;----》建议大写
赋值:
变量名=值;—》等号前后不能有空格。
引用:
${变量名} / $变量名(存在风险:如果变量名和之后的字符串紧挨着时,会被视为一个新的变量名);
建议:引用变量值时,以{}的形式引用比较安全。

4-2:位置变量
理解为:变量的值可以从命令行传入
我们怎样使用传入进来的数值?—》 和数字进行结合: 和数字进行结合: 和数字进行结合:N (N 是数字,从0开始)
4-3:系统预定义变量
系统之前规定将$符号和指定的另外一个字母两者相结合,之后具备一定的含义。
$#:统计命令行参数的个数(有效参数的个数–》不包含可执行文件的名字)
$@:统计传入的参数本身(有效参数)
$*:统计传入的参数本身(有效参数)
$?:获取上一条命令的退出状态:状态数值不是0就是1(0代表上一条命令时成功执行,反之为失败执行)

4-4:环境变量
—》查看系统中的所有环境变量的命令:env
操作:
添加新环境变量:
export 变量名=值 (可以一次性赋值多个,多个数值之间以冒号隔开)
修改环境变量值:
export 变量名=新值: 变量名 / e x p o r t 变量名 = {变量名} / export 变量名= 变量名/export变量名={变量名} :新值
清除环境变量:
unset 变量名

5、脚本的语句

1、说明性语句

凡是以#开头的语句都是说明性语句,可以理解为注释。

2、功能性语句
(1)动态获取变量的值

命令:read —>功能类似于scanf函数的功能
方式:
read 变量名1 变量名2 。。。 变量名 (定义变量且赋初值)
提示语句的写法:
(1)方式1:通过echo “提示语句”
(2)方式2:使用read的同时去打印:read -p “提示语句” 变量名

(2)通配符的转义

软转义:
双引号 和 不加双引号 —》软转义(会保留shell特殊字符的含义)
eg:
在打印输出的时,遇到 变量名时,会先执行 {变量名}时,会先执行 变量名时,会先执行{变量名},访问到数值之后再去输出。还有遇到命令的时候,也会先执行命令,将命令执行之后的结果再去输出。

   总结:
        双引号和不加双引号在多数情况下,两者没啥区别,但是又不完全等价。
        具体区别有两种情况:
        情况1:含有多个空格
        比如:
             echo  “wangjia         jia”;  --->原样输出
             echo  wangjia         jia; ---》wangjia jia  (中间只会有一个空格,将其他空格全都抹掉)
        情况2:在含有\
        

     综上所述:在以后输出时,建议都加上双引号,结果一定符合预期数值。

硬转义:
单引号 和 \ —》硬转义(会屏蔽掉shell特殊字符含义)
eg:
在打印输出时,遇到 变量名或者命令时,不会去执行 {变量名}或者命令时,不会去执行 变量名或者命令时,不会去执行{变量名}取到数值或者执行命令,反而将它们视为一个字符串去直接输出。

(3)expr算术运算命令

功能: + - * / %
方式:
expr 变量名1 运算符 变量名2

总结:
expr和变量名之间至少保留1个空格,运算符和两个变量名之间也要存在空格。

(4)测试命令test

测试命令---->代替判断语句
测试格式:
第一种: [ 对象1 测试条件 对象2 ]
第二种: test 对象1 测试条件 对象2
测试对象:

1、整形:测试对象假设为:num1 num2
等于: num1 -eq num2
不等于:num1 -ne num2
大于: num1 -gt num2
大于等于:num1 -ge num2
小于: num1 -lt num2
小于等于: num1 -le num2
等于:equal 不等于:not equal
大于:greater than 大于等于 :greater than equal
小于:less than 小于等于:less than equal

2、字符串:测试对象假设为:str1 str2
等于: str1 = str2 (空格必须有,否则被视为变量赋值)
不等于:str1 != str2
字符串长度为0: -z str1/str2
字符串长度不为0:-n str1/str2
3、文件属性:假设测试对象为:f1 f2
-f :测试对象是否存在且为普通文件
-d :测试对象是否存在且为目录文件
-L :测试对象是否存在且为软链接文件(符号链接文件)
-r:测试对象是否存在且具备可读权限
-w:测试对象是否存在且具备可写权限
-x:测试对象是否存在且具备可执行权限
-s:测试对象的大小
f1 -nt f2:测试f1是否比f2更新(文件最后一次保存的时间)
f1 -ot f2:测试f1是否比f2更旧(文件最后一次保存的时间)

3、结构性语句
1、顺序结构

2、选择结构

多路分支:
if:
形式参考如下:
if 测试语句
then
命令表
elif 测试语句
then
命令表
。。。
else
命令表
fi

case:
形式参考如下:
case 变量值 in
模式1)
命令表
;;
模式2)
命令表
;;

*)
命令表
esac

注意:
在脚本中:与使用-a表示,或使用-o表示,非使用!表示

3、循环结构

for while
C中:
Int i = 0;
for(;表达式2;表达式3)
{
循环体
}

脚本中的for循环:
形式1:带参数列表
for 变量名 in 单词列表
do
命令表
done

seq:产生指定范围之内的所有整数。
用法:
seq 范围1 范围2

脚本中的while循环。
格式:
while 条件判断
do
命令表
done

死循环:
while [ true ]
do
命令表
done

6、脚本的函数

函数定义格式:
function_name()
{
命令表1
命令表2
。。。
命令表N
}

如何调用这个函数:
方式两种:
方式1: 变量名 = function_name 参数1 参数2 。。。。
—》查看变量名的值来获取函数调用的输出信息
方式2:function_name 参数1 参数2 。。。。。。
—》查看函数的执行结果通过打印$?

如何在子函数内部访问实参?
通过$N这样的结合访问(N是数字)

注意:
1、shell函数中的返回值范围是0-255之间,超过255从0开始重新取值(环回),但是子函数内部没有限制。
2、对于实参,可以通过位置变量来进行传参。—》$N来访问传递给实参的数值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值