Linux工具

目录

1. 学习yum工具,进行软件安装

1.1. 查看软件包

1.2. 安装软件

1.3. 卸载软件

2. 掌握vim编辑器使用,学会vim的简单配置

2.1. vim的配置

3. 掌握gcc/g++编译器的使用,并了解其过程,原理

3.1. 预处理

3.2. 编译

3.3. 汇编

3.4. 链接

3.5. 感性的理解动静态库

3.5.1. 如何理解动态链接和静态链接: 

3.5.2. 如何操作

4. 掌握简单gdb调试器

5. 掌握简单的makefifile编写,了解其运行思想

5.1. 如何编写makefile

5.2.  如何理解"伪目标"

5.3. 如何实现多文件的makefile

6. 编写自己的第一个Linux 程序:进度条

6.1.输出缓冲区的简单认识

6.2. 回车和换行的区别

6.3. 简单进度条的实现

7. 学习 git 命令行的简单操作, 能够将代码上传到 Github 上

1. 什么是版本控制

2. git的创造者

3. git的操作(git三板斧)


1. 学习yum工具,进行软件安装

Linux 下安装软件 , 一个通常的办法是下载到程序的源代码 , 并进行编译 , 得到可执行程序 .
但是这样太麻烦了 , 于是有些人把一些常用的软件提前编译好 , 做成软件包 ( 可以理解成 windows 上的安装程序) 放在一个服务器上 , 通过包管理器可以很方便的获取到这个编译好的软件包 , 直接进行安装 .
软件包和软件包管理器 , 就好比 "App" " 应用商店 " 这样的关系 .
yum(Yellow dog Updater, Modifified) Linux 下非常常用的一种包管理器 . 主要应用在 Fedora, RedHat, Centos等发行版上 .
一般原生的Linux操作系统,内置的下载链接基本都是自己配套的国外的网址
yum的两个功能:a. 搜索、安装、卸载;b. 解决依赖关系

yum源:就是一个配置文件,/etc/yum.repos.d

# 扩展yum源:
sudo yum install -y epel-release
# 更新yum源
1. 进入 /etc/yum.repos.d
2. 备份CentOS-Base.repo
3. 下载新的配置文件(阿里)
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
或者下载新的配置文件(腾讯) 
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.cloud.tencent.com/repo/centos7_base.repo 
#如果没有wget命令
sudo yum -y install wget
4. 清除旧的yum缓存并生成新的yum缓存
yum clean all
yum makecache
5. 更新
yum update -y

1.1. 查看软件包

通过 yum list 命令可以罗列出当前一共有哪些软件包, 由于包的数目可能非常之多 , 这里我们需要使用 grep 命令只筛选出我们关注的包,例如:
[Xq@VM-24-4-centos 8_31]$ yum list | grep sl.x86_64
Repository epel is listed more than once in the configuration
sl.x86_64                                5.02-1.el7                    @epel 
软件包名称 : 主版本号 . 次版本号 源程序发行号 - 软件包的发行号 主机平台 .cpu 架构 .
"x86_64" 后缀表示 64 位系统的安装包
"el7" 表示操作系统发行版的版本 . "el7" 表示的是 centos7/redhat7.
"el6" 表示 centos6/redhat6.
epel 表示的是 " 软件源 " 的名称

1.2. 安装软件

注意事项 :
安装软件时由于需要向系统目录中写入内容 , 一般需要 sudo 或者切到 root 账户下才能完成 .
yum 安装软件只能一个装完了再装另一个 . 正在 yum 安装一个软件的过程中 , 如果再尝试用 yum 安装另外一个软件, yum 会报错
# -y可以简单的理解为:不要问我安装的一些其他事项了
sudo yum install -y sl.x86_64   

1.3. 卸载软件

sudo yum -y remove sl.x86_64

2. 掌握vim编辑器使用,学会vim的简单配置

什么是vim? vim是一个编辑器,vim是一个多模式的编辑器;

vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。

在这里讲解vim的三种模式 ( 其实有好多模式,目前掌握这 3 种即可 ), 分别是命令模式( command mode )、插入模式(Insert mode )和底行模式( last line mode ),各模式的功能区分如下:
正常 / 普通 / 命令模式 (Normal mode)
控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入 Insert mode 下,或者到 last line mode
插入模式 (Insert mode)
只有在 Insert mode 下,才可以做文字输入,按「 ESC 」键可回到命令行模式。该模式是我们后面用的最频繁的编辑模式。
底行模式 (last line mode)
文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。 在命令模式下, shift+: 即可进入该模式。
要查看你的所有模式:打开vim ,底行模式直接输入 :help vim-modes

命令模式下,一些常见命令:

yy:将光标所在的当前行复制;如果是 nyy,就是从光标这一行开始复制n行

p:将复制进行粘贴工作,如果是np,复制n次

dd: 删除(剪切)当前光标所在行,如果是ndd,则从当前光标所在行开始,删除(剪切)n行

u:撤销操作

Ctrl + r: 撤销u操作

shift + g:光标定位到文件的结尾

gg: 光标定位到文件的开始

n + shift + g:光标定位到文件的任意行(n要合法n>=1&&n<=文件的结尾)

shift + 6(^): 光标所在行的起始位置

shift + 4($):光标所在行的末尾位置

w,b:以单词为单位进行光标的前后移动,w向前走,b向后走

h、j、k、l:分别代表左、下、上、右;

一般情况h在键盘的最左边 --- 左,l在键盘的最右边 --- 右

j(可以理解为jump,向下跳) --- 下,k(可以理解为king,至高无上) --- 上

shift + ~: 大小写切换

shirt + r: 进入替换模式(replace mode)

r : 替换当前光标所在的字符,也支持nr,即从当前光标开始,替换n个字符

x:删除光标当前所在的字符

X(shift + x):删除光标的前一个字符,也支持nx;

g + d:高亮显示所有相同的单词

shift + *:向下查找相同单词并高亮显示

shift + # :向上查找相同单词并高亮显示

先按gg(先回到顶行),然后ggvG或者ggVG:全选高亮显示

先按gg(先回到顶行),然后ggyG : 全部复制

先按gg(先回到顶行),然后dG: 全部删除

在底行模式下:

set nu or set nonu:打开vim中的行号或者取消行号

vs 文件名 打开多个文件,可以做到分屏操作

Crtl + ww 依次向后切换窗口

Crtl + w + k/j/h/l 上下左右切换窗口

w:写入(保存)  w!  强制保存  

q:退出  q!强制退出

wq!:强制保存且退出

:!cmd : 不退出vim执行对应的cmd命令(执行命令行,编译、运行、查看man手册等)

noh:取消高亮显示

/ "key" + 回车 :从上往下搜索,按n找下一个关键字

? "key"  + 回车: 从下往上搜索,按n找上一个关键字

%s/要替换的内容/替换后的内容/: 执行替换命令,% 表示对整个文件进行替换, s表示替换操作

2.1. vim的配置

配置文件的位置
在目录 /etc/ 下面,有个名为 vimrc 的文件(/etc/vimrc),这是系统中公共的 vim配置文件,对所有用户都有效。例如, /root 目录下,通常已经存在一个vimrc 文件 , 如果不存在,则创建之。
而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:.vimrc
进入自己的主工作目录 , 执行 cd ~,打开自己目录下的.vimrc 文件,执行 vim .vimrc       
常用配置选项 , 用来测试
设置语法高亮 : syntax on
显示行号 : set nu
取消行号:set nonu
设置缩进的空格数为 4: set shiftwidth=4
突出显示当前行:set cursorline

3. 掌握gcc/g++编译器的使用,并了解其过程,原理

gcc(g++)是专门用来编译链接C(C++)的编译器

gcc只用用来编译C,g++可以编译C也可以编译C++

但更推荐的是gcc编译C,g++编译C++

g++ -v 查看g++版本

如果云服务器里面没有g++

sudo yum install -y gcc-c++ 

程序的翻译过程本质做的事情就是将文本内容翻译为二进制指令。

分为四个过程:

1.预处理

2.编译

3.汇编

4.链接

接下来以gcc为例,感性认识我们的gcc编译器:

# 以如下的C代码为例
#include <stdio.h>
#define NUM 100
int main()
{
  printf("%d\n",NUM);
  //printf("haha1\n");
  //printf("haha2\n");
  //printf("haha3\n");
  printf("hehe\n");
#ifdef OBJ_PTR
  printf("hello obj_ptr\n");
#else 
  printf("hello obj_ref\n");
#endif
  return 0;
}

3.1. 预处理

a. 去掉注释;b. 宏替换;c. 头文件展开;d. 条件编译等

[Xq@VM-24-4-centos 8_29]$ gcc -E test.c -o test.i
[Xq@VM-24-4-centos 8_29]$ ll
total 24
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:18 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:21 test.i
[Xq@VM-24-4-centos 8_29]$
选项 “-E”, 该选项的作用是让 gcc 在预处理结束后停止编译过程。
选项 “-o” 是指目标文件 ,test.c经过预处理后生成的文件是test.i

可以看到,经过预处理后,头文件被展开了(我们自己写的代码就18行,经过预处理后的代码量已经达到了800多行,多出来的就是头文件展开的内容),并且我们定义的宏被替换了,注释被去掉了,条件编译也干掉了。经过预处理后,此时依旧还是C语言。

3.2. 编译

在这个阶段中 ,gcc 首先要检查代码的规范性、是否有语法错误等 , 以确定代码的实际要做的工作 , 在检查无误后,gcc 把c代码翻译成汇编语言
[Xq@VM-24-4-centos 8_29]$ ll
total 24
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:30 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:30 test.i
[Xq@VM-24-4-centos 8_29]$ gcc -S test.i -o test.s
[Xq@VM-24-4-centos 8_29]$ ll
total 28
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:30 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:30 test.i
-rw-rw-r-- 1 Xq Xq   585 Aug 29 20:30 test.s
[Xq@VM-24-4-centos 8_29]$

用户可以使用“-S”选项来进行查看,从现在开始进行程序的翻译,如果编译完成,就停下来。

此时我们就生成了汇编代码。 

3.3. 汇编

汇编代码 -> 可重定位的目标二进制文件

[Xq@VM-24-4-centos 8_29]$ ll
total 28
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:30 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:30 test.i
-rw-rw-r-- 1 Xq Xq   585 Aug 29 20:33 test.s
# 从现在开始进行程序的翻译,如果汇编完成,就停下来
[Xq@VM-24-4-centos 8_29]$ gcc -c test.s -o test.o
[Xq@VM-24-4-centos 8_29]$ ll
total 32
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:30 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:30 test.i
-rw-rw-r-- 1 Xq Xq  1664 Aug 29 20:35 test.o
-rw-rw-r-- 1 Xq Xq   585 Aug 29 20:33 test.s
[Xq@VM-24-4-centos 8_29]$

用户在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了 

此时就将汇编代码->可重定向(位)的目标二进制文件

但此时这个二进制文件是不可以被执行的,这个文件类似于windows上的(*.obj文件),

因为此时你只是将你写的东西转化成了二进制,你调用库的东西还需要经过链接操作处理。

[Xq@VM-24-4-centos 8_29]$ ll
total 32
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:30 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:30 test.i
-rw-rw-r-- 1 Xq Xq  1664 Aug 29 20:35 test.o
-rw-rw-r-- 1 Xq Xq   585 Aug 29 20:33 test.s
# 不可被执行
[Xq@VM-24-4-centos 8_29]$ ./test.o
-bash: ./test.o: Permission denied
# 此时就算我们为这个文件加上x权限,依旧不可执行
[Xq@VM-24-4-centos 8_29]$ chmod u+x test.o
[Xq@VM-24-4-centos 8_29]$ ./test.o 
-bash: ./test.o: cannot execute binary file
[Xq@VM-24-4-centos 8_29]$

3.4. 链接

多个.o(windows下是.obj)文件形成一个可执行文件

[Xq@VM-24-4-centos 8_29]$ ll
total 32
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:30 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:30 test.i
-rw-rw-r-- 1 Xq Xq  1664 Aug 29 20:35 test.o
-rw-rw-r-- 1 Xq Xq   585 Aug 29 20:33 test.s
# 链接后生成可执行程序
[Xq@VM-24-4-centos 8_29]$ gcc test.o -o my_test
[Xq@VM-24-4-centos 8_29]$ ./my_test 
100
hehe
hello obj_ref
[Xq@VM-24-4-centos 8_29]$

为了我们方便记忆:程序翻译的过程的这些选项可以看作"ESc"。

对应的文件后缀可以记作"iso",iso镜像文件的后缀。

3.5. 感性的理解动静态库

一般链接的过程是有两种方式的:

a. 动态链接 - 需要动态库(Linux下为.so)如果是在windows下(.dll)

b. 静态链接 - 需要静态库(Linux下为.a)如果是在windows下(.lib)

[Xq@VM-24-4-centos 8_29]$ ll
total 44
-rwxrwxr-x 1 Xq Xq  8408 Aug 29 20:43 my_test
-rw-rw-r-- 1 Xq Xq   263 Aug 29 20:30 test.c
-rw-rw-r-- 1 Xq Xq 16925 Aug 29 20:30 test.i
-rw-rw-r-- 1 Xq Xq  1664 Aug 29 20:35 test.o
-rw-rw-r-- 1 Xq Xq   585 Aug 29 20:33 test.s
[Xq@VM-24-4-centos 8_29]$ ldd my_test  # 查看这个可执行程序所依赖的库
	linux-vdso.so.1 =>  (0x00007ffce65f5000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f013fba9000)  # C语言动态库
	/lib64/ld-linux-x86-64.so.2 (0x00007f013ff77000)
[Xq@VM-24-4-centos 8_29]$ file my_test 
my_test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c3390b0475973b5725f86fc5b8f5a473e3462d08, not stripped
[Xq@VM-24-4-centos 8_29]$

 在我们以前写C代码的时候,我们时常会用printf这个打印函数,虽然我们经常使用它,但我们知道这个函数是C提供的并不是我们自己实现的。那么这个printf是谁定义的呢?

[Xq@VM-24-4-centos obj_tmp]$ ll
total 0
[Xq@VM-24-4-centos obj_tmp]$ vim test.c
[Xq@VM-24-4-centos obj_tmp]$ cat test.c 
#include <stdio.h>

int main()
{
  printf("haha\n");
  return 0;
}
[Xq@VM-24-4-centos obj_tmp]$
vim /usr/include/stdio.h # 查看#include<stdio.h>这个头文件 

我们发现,printf这个方法在stdio.h这个文件里面只有声明,而没有定义;

那我就很好奇,谁提供了printf这种方法的定义呢?

# 提供了C语言的方法实现
[Xq@VM-24-4-centos obj_tmp]$ ls /lib64/libc-2.17.so -l
-rwxr-xr-x 1 root root 2156592 May 19  2022 /lib64/libc-2.17.so
# 提供了C语言的方法声明
[Xq@VM-24-4-centos obj_tmp]$ ls /usr/include/stdio.h -l
-rw-r--r-- 1 root root 31641 May 18  2022 /usr/include/stdio.h
[Xq@VM-24-4-centos obj_tmp]$ ldd a.out 
	linux-vdso.so.1 =>  (0x00007fffd33e0000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fb3d9cd9000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fb3da0a7000)
[Xq@VM-24-4-centos obj_tmp]$ ls /lib64/libc.so.6 -l
lrwxrwxrwx 1 root root 12 Jul 25  2022 /lib64/libc.so.6 -> libc-2.17.so
[Xq@VM-24-4-centos obj_tmp]$
最后的答案是 : 系统把这些函数实现都被做到名为/lib64/ libc.so.6 的库文件中去了 , 在没有特别指定时 ,gcc 会到系统默认的搜索路径“/usr/lib” 下进行查找 , 也就是链接到 libc.so.6 库函数中去 , 这样就能实现函数“printf” , 而这也就是链接的作用。
3.5.1. 如何理解动态链接和静态链接: 
静态库是指编译链接时 , 把库文件的代码全部拷贝到我们的可执行文件中 , 因此生成的文件比较大 , 但在运行时也就不再需要库文件了,较动态链接相比,占用更多的资源。其后缀名一般为.a
动态库与之相反 , 在编译链接时并没有把库文件的代码加入到可执行文件中 ,而是将我要的方法的地址(动态库的地址),填入到我的可执行程序中,建立关联。 这样可以节省系统的开销。动态库一般后缀名为. so
如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后 ,gcc 就可以生成可执行文件。
动态链接形成的可执行程序,最终是依赖于这个库文件的,如果此时库文件缺失,那么会导致这个可执行程序无法运行。带来的好处就是,一定程度上节省了资源。
静态链接形成的可执行程序,因为它会把库文件中对应的方法实现全部拷贝这个可执行程序文件里,因此形成的可执行程序不依赖于库文件。那么带来的问题就是,占用资源变多(多个文件可能会导致产生大量重复代码);
3.5.2. 如何操作
[Xq@VM-24-4-centos obj_tmp]$ ll
total 4
-rw-rw-r-- 1 Xq Xq 67 Aug 29 21:03 test.c
[Xq@VM-24-4-centos obj_tmp]$ gcc test.c -o my_test
[Xq@VM-24-4-centos obj_tmp]$ ldd my_test 
	linux-vdso.so.1 =>  (0x00007ffefad66000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fa80d9c4000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa80dd92000)
[Xq@VM-24-4-centos obj_tmp]$ file my_test 
my_test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ed2fa6127468e8640b3da2cf95217f753eab25fd, not stripped
[Xq@VM-24-4-centos obj_tmp]$

gcc/g++默认生成的可执行程序是动态链接的。

[Xq@VM-24-4-centos obj_tmp]$ ll
total 16
-rwxrwxr-x 1 Xq Xq 8360 Aug 29 22:02 my_test
-rw-rw-r-- 1 Xq Xq   67 Aug 29 21:03 test.c
# 添加-static选项,就可以达到静态链接的效果
[Xq@VM-24-4-centos obj_tmp]$ gcc test.c -o my_test_static -static
[Xq@VM-24-4-centos obj_tmp]$ ll
total 860
-rwxrwxr-x 1 Xq Xq   8360 Aug 29 22:02 my_test
-rwxrwxr-x 1 Xq Xq 861288 Aug 29 22:04 my_test_static
-rw-rw-r-- 1 Xq Xq     67 Aug 29 21:03 test.c
[Xq@VM-24-4-centos obj_tmp]$ ldd my_test_static 
	not a dynamic executable
[Xq@VM-24-4-centos obj_tmp]$ file my_test_static 
my_test_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=2b8aa38e0ad6283ee8b71f61e1343352fa12eaf3, not stripped
[Xq@VM-24-4-centos obj_tmp]$

给gcc/g++添加-static选项:表明使用静态链接的方式生成可执行程序

# 动态链接必须使用.so动态库文件
# 静态链接必须使用.a动态库文件
# 如果没有静态库
sudo yum install -y glibc-static      # C静态库
sudo yum install -y libstdc++-static  # C++静态库

4. 掌握简单gdb调试器

程序的发布方式有两种, debug 模式(可以调试)和 release 模式(不可调试)
Linux gcc/g++默认形成 出来的二进制程序,是 release 模式。
补充:默认链接方式:动态链接,要以静态链接(-static)
要使用 gdb 调试,必须在源代码生成二进制程序的时候 , 加上 - g 选项,以debug模式发布。
# 查看可执行程序的命令,用来显示一个或多个elf格式的目标文件的信息
readelf -S 目标文件
# release模式生成的可执行程序之所以不可以被调试,原因是:没有debug信息
# 开始调试
gdb 对应的可执行程序

list/l 行号:显示源代码(从当前行开始),接着上次的位置往下列,每次列10行。
# gdb会记录最近的一次命令,如果命令不发生变化,回车即可
list/l 函数名:列出某个函数的源代码。
r/run:运行程序,开始调试,如果没有设置断点,运行完自动退出程序
n/next:单条执行(类似于vs的F10---逐过程)
s/step:进入函数调用(类似于vs的F11---逐语句)
break(b) 行号:在某一行设置断点(类似于vs的F9---设置断点)
break 函数名:在某个函数开头设置断点
info b/break :查看断点信息 
finish:执行完当前函数,然后停下来等待命令
continue(或c):运行至下一个断点就停下来(若没有断点,则跑完程序)
d/delete breakpoints:删除所有断点
d/delete n: 删除编号(不是行号)为n的断点
disable 断点编号:禁用断点       #Enb 断点是否被启用 y --- 启用  n --- 禁用
enable 断点编号:启用断点
display 变量名:长显示,跟踪查看一个变量,每次停下来都显示它的值
undisplay:取消对先前设置的那些变量的跟踪
print(表达式p):打印表达式p的值,通过表达式可以修改变量的值或者调用函数,eg: print(p) = 50
p 变量:打印变量内容。
set var:修改变量的值   eg:  set val i=5
until X行号:跳至X行(更多是函数内部局部性跳过)
bt/breaktrace:查看各级函数调用及参数
info(i) locals:查看当前栈帧局部变量的值
quit:退出gdb

5. 掌握简单的makefile编写,了解其运行思想

会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
makefile带来的好处就是——“ 自动化编译 ,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE 都有这个命令,比如:Delphi make Visual C++ nmake Linux GNU make。可见,makefile都成为了一种在工程方面的编译方法。
make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。

make 是一条命令,makefile 是一个文件,作用是自动化构建项目

make的原理:
make 是如何工作的 , 在默认的方式下,也就是我们只输入 make 命令。那么,
1. make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2. 如果找到,它会找文件中的第一个目标文件( target ), 并把这个文件作为最终的目标文件。
3. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理。
4. make 只管文件的依赖性,即如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

 项目清理:

工程是需要被清理的
clean 这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make 执行。即命令 ——“make clean” ,以此来清除所有的目标文件,以便重新编译。
但是一般我们这种 clean 的目标文件,我们将它设置为伪目标 , .PHONY 修饰 , 伪目标的特性是,总是被执行的。

5.1. 如何编写makefile

我们需要实现两个东西,a. 依赖关系;b. 依赖方式

my_test:test.cc    #代表依赖关系
	g++ test.cc -o my_test    #必须以第一列开始,然后Tab开头,编写依赖方法

# .PHONY修饰的clean称之为伪目标
# 伪目标即总是被执行的
.PHONY:clean
clean:                # 清理文件,依赖关系无
	rm -f my_test     # 代表依赖方法
[Xq@VM-24-4-centos 8_30]$ ll
total 8
-rw-rw-r-- 1 Xq Xq 76 Aug 30 15:58 makefile
-rw-rw-r-- 1 Xq Xq 92 Aug 30 15:56 test.cc
# make默认只会形成地一个可执行文件(自上向下扫描),在这形成的就是my_test
[Xq@VM-24-4-centos 8_30]$ make
g++ test.cc -o my_test
[Xq@VM-24-4-centos 8_30]$ ll
total 20
-rw-rw-r-- 1 Xq Xq   76 Aug 30 15:58 makefile
-rwxrwxr-x 1 Xq Xq 8968 Aug 30 16:32 my_test
-rw-rw-r-- 1 Xq Xq   92 Aug 30 15:56 test.cc
[Xq@VM-24-4-centos 8_30]$

5.2.  如何理解"伪目标"

[Xq@VM-24-4-centos 8_30]$ ll
total 8
-rw-rw-r-- 1 Xq Xq 76 Aug 30 15:58 makefile
-rw-rw-r-- 1 Xq Xq 92 Aug 30 15:56 test.cc
[Xq@VM-24-4-centos 8_30]$ make
g++ test.cc -o my_test
[Xq@VM-24-4-centos 8_30]$ ll
total 20
-rw-rw-r-- 1 Xq Xq   76 Aug 30 15:58 makefile
-rwxrwxr-x 1 Xq Xq 8968 Aug 30 16:40 my_test
-rw-rw-r-- 1 Xq Xq   92 Aug 30 15:56 test.cc
# 编译器告诉我my_test这个目标文件已经是最新的了,而不需要在执行
[Xq@VM-24-4-centos 8_30]$ make
make: `my_test' is up to date.
[Xq@VM-24-4-centos 8_30]$ make
make: `my_test' is up to date.
# 但是make clean却能一直执行,因为clean这个目标文件被.PHONY修饰了即伪目标(总是被执行的)
# 即总是会根据依赖关系,执行依赖方法 
[Xq@VM-24-4-centos 8_30]$ make clean;
rm -f my_test
[Xq@VM-24-4-centos 8_30]$ make clean
rm -f my_test

5.3. 如何实现多文件的makefile

[Xq@VM-24-4-centos 8_30]$ ll
total 12
-rw-rw-r-- 1 Xq Xq 65 Aug 30 21:38 code.c
-rw-rw-r-- 1 Xq Xq 47 Aug 30 21:38 code.h
-rw-rw-r-- 1 Xq Xq  0 Aug 30 21:34 makefile
-rw-rw-r-- 1 Xq Xq 58 Aug 30 21:38 test.c
[Xq@VM-24-4-centos 8_30]$ cat code.h
#pragma once
#include <stdio.h>

extern void print();
[Xq@VM-24-4-centos 8_30]$ cat code.c
#include "code.h"

void print()
{
  printf("cowsay hello!\n");
}
[Xq@VM-24-4-centos 8_30]$ cat test.c 
#include "code.h"

int main()
{
  print();
  return 0;
}
[Xq@VM-24-4-centos 8_30]$

上面有三个文件,分别是code.c,code.h,test.c,内容分别为方法实现、方法声明、主函数 那么此时makefile如何实现呢?  

# 因为此时没有test.o和code.o,因此make会自动向下寻找test.o和code.o的实现
my_test:test.o code.o    
	gcc test.o code.o -o my_test   # -o 必须要跟上我们的可执行文件名
# 即先生成这两个.o文件,在生成my_test
test.o:test.c 
	gcc -c test.c -o test.o
code.o:code.c
	gcc -c code.c -o code.o
.PHONY:clean
clean:
	rm -f *.o my_test

# 结果符合预期,先形成两个.o文件,在生成我们的可执行文件
[Xq@VM-24-4-centos 8_30]$ make
gcc -c test.c -o test.o
gcc -c code.c -o code.o
gcc test.o code.o -o my_test

5.4. makefile的特殊实现

my_test:test.c
	gcc -o $@ $^
#解释: 这是一种简写的方式:
# $@ 代表这个依赖关系的目标文件,即my_test
# $^ 代表着冒号后面的所有文件,在这里就是test.c

.PHONY:clean
clean:
	rm -f my_test

6. 编写自己的第一个Linux 程序:进度条

6.1.输出缓冲区的简单认识

准备工作:

# 一个休眠函数,参数的单位是秒  man 3 sleep
#include <unistd.h>
unsigned int sleep(unsigned int seconds);

# 立即刷新一个流的缓冲区  man 3 fflush
#include <stdio.h>
int fflush(FILE *stream);

这里有一段代码:

# makefile的实现
[Xq@VM-24-4-centos obj_dir]$ cat makefile 
my_test:test.c
	gcc -o my_test test.c
.PHONY:clean
clean:
	rm -f test
# 源代码
[Xq@VM-24-4-centos obj_dir]$ cat test.c 
#include <stdio.h>
#include <unistd.h>

int main()
{
  printf("you can see me!\n");
  sleep(3);
  return 0;
}
[Xq@VM-24-4-centos obj_dir]$

[Xq@VM-24-4-centos obj_dir]$ ll
total 20
-rw-rw-r-- 1 Xq Xq   73 Aug 30 22:33 makefile
-rwxrwxr-x 1 Xq Xq 8408 Aug 30 22:36 my_test
-rw-rw-r-- 1 Xq Xq  110 Aug 30 22:36 test.c
[Xq@VM-24-4-centos obj_dir]$ ./my_test 
you can see me!
[Xq@VM-24-4-centos obj_dir]$

如果此时我们形成可执行程序,现象是什么?

是先看到打印的语句还是先休眠?结果是我们先看到打印的语句然后再休眠。

但如果我们改一下代码:

#include <stdio.h>
#include <unistd.h>

int main()
{
  printf("you can see me!");  # 将\n给删掉
  sleep(3);
  return 0;
}

[Xq@VM-24-4-centos obj_dir]$ ll
total 20
-rw-rw-r-- 1 Xq Xq   73 Aug 30 22:33 makefile
-rwxrwxr-x 1 Xq Xq 8408 Aug 30 22:33 my_test
-rw-rw-r-- 1 Xq Xq  108 Aug 30 22:34 test.c
[Xq@VM-24-4-centos obj_dir]$ make clean;make
rm -f my_test
gcc -o my_test test.c
[Xq@VM-24-4-centos obj_dir]$ ./my_test 
you can see me![Xq@VM-24-4-centos obj_dir]$

现象是:打印的语句并没有立即显示,而是先休眠了3秒钟,此时进程结束的时候,打印的语句才会被显示。

分析:但是我有个疑问,C语言的执行流默认不是从上向下执行吗?这里也没什么条件判断,怎么会先执行sleep,在执行printf。

首先,这里一定是先执行printf,在执行sleep,那为什么我们看到的现象与之相反呢,原因是因为,printf是语言级别的接口,默认是向显示器写入内容的,而在写之前,会将内容写入C语言级别的输出缓冲区里,而'\n'是会将缓冲区里面的内容刷新到内核缓冲区里,再写入显示器文件,而如果没有'\n',那么此时这个打印的内容会暂时再语言级别的缓冲区里,当进程结束时才会将语言级别的缓冲区里的内容刷新到内核缓冲区里,这也就是为什么我们先看到sleep,在看到打印的内容。

那如果我此时就想立即刷新内容,该如何实现?

#include <stdio.h>
#include <unistd.h>

int main()
{
  printf("you can see me!");
  fflush(stdout);   # 调用fflush,立即刷新输出缓冲区里面的内容
  sleep(3);
  return 0;
}

[Xq@VM-24-4-centos obj_dir]$ ./my_test 
you can see me![Xq@VM-24-4-centos obj_dir]$

此时的现象: 我们是先看到打印内容,在看到sleep的,符合预期

6.2. 回车和换行的区别

换行('\n'):新起一行,但列不变。

回车('\r'):回到这行的最开始。

语言上的'\n'经过特殊处理, 被当作了回车+换行

6.3. 简单进度条的实现

#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define NUM 102    # 进度条的长度

int main()
{
  char str[NUM];
  memset(str,0,sizeof(str));
  const char* sign = "|/-\\";    # 动态标志
  int count = 0;
  while(count <= 100)
  {
    # -100左对齐且输出宽度为100
    # %% 进行转义得到一个 %
    # 在这里因为实际需求需要'\r' 回车,'\n'不符合需求
    # C语言输出彩色字体:
    # 背景色 + 字体颜色 
    # printf("\033[背景色;字体颜色m 打印的内容 \033[0m");
    # 如果只想要背景色:
    # printf("\033[背景色m 打印的内容 \033[0m");
    printf("\033[47;33m[%-100s][%d%%] %c \033[0m \r",str,count,sign[count%4]);
    str[count++] = '=';
    # 因为上面的打印没有'\n',不会刷新缓冲区,因此在这里调用fflush,立即刷新C的输出缓冲区
    fflush(stdout);
    # usleep与sleep差别在于 前者单位是微秒,后者单位是秒
    # 50000微秒 == 0.05秒
    usleep(50000);
  }
  printf("\n");
  return 0;
}

补充:

# '\033' 是一个8进制转义字符,'控制终端中文字的颜色'  
# printf("\033[字符显示的方式;字符的颜色;字符的背景颜色m 内容\033[m");
# 最后的 "\033[m" 表示结束颜色作用域
/*
***字符显示的方式
***0:关闭所有属性
***1:字体高亮
***2:字体低亮
***4:字体下划线
***5:字体闪烁
***7:字体反显
***8:隐藏文字

***背景色: 40 --- 47
***40:黑
***41:深红
***42:绿
***43:黄色
***44:蓝色
***45:紫色
***46:深绿
***47:白色

***字体颜色: 30 --- 39
***30:黑
***31:红
***32:绿
***33:黄
***34:蓝色
***35:紫色
***36:深绿
***37:白色
*/

7. 学习 git 命令行的简单操作, 能够将代码上传到 Github 上

1. 什么是版本控制

版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。

版本控制最主要的功能就是追踪文件的变更。即对历史版本进行修改前,会对历史版本进行备份,在对历史版本进行修改,且此时文件的版本号都会增加。

版本控制器:git与svn

2. git的创造者

雷纳斯托瓦兹,自己着手写了一个版本控制器,就是现在的git

3. git的操作(git三板斧)

# 查看git的版本
git --version
# 如果git命令没安装
sudo yum install -y git.x86_64

 1.在gitee新建一个仓库

# 预备(克隆):将远程git仓库克隆到本地,默认在当前路径下
git clone 仓库链接

# 第一步
# 添加到本地仓库
git add 对应的文件

# 第二步
# 提交代码到本地仓库中
git commit -m "这里必须提交日志及相关信息"

# 第三步
# 提交本地仓库的变化到远程git仓库
git push

# 查看日志
git log

#将远程git仓库内容同步到本地仓库(更新本地仓库)
git pull

#用于显示工作目录和暂存区的状态
git status

#先删除本地仓库的文件,继续三板斧,就可以删除远程git仓库文件
git rm 对应的文件

#特殊情况
# 第一次使用时,需要你配置邮箱和用户名
git config --global user.email "your email"
git config --global user.name "your name"

#如果不想提交某些后缀的文件同步到远程git仓库,就添加到.gitignore里(黑名单,在这里面就不上传)
# .gitignore
eg: 将后缀为.o、.obj文件添加到.gitignore,那么*.o、*.obj文件就不会上传到远程git仓库
*.o
*.obj

# 如果git的commit写错了怎么改
git commit --amend 进入vim窗口 进入编辑模式,更改对应错误信息 保存退出即可
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值