目录
8.1 使用 (()) 进行算术运算----->常用于算术运算
一.引言
本文总结了c高级所包括的终端指令、shell编程、Makefile的内容,在终端指令中我们将学到Ubuntu中的各种常用指令,包括对文件的操作,磁盘的操作,用户的操作等。shell编程对大部分人来说是一门新语言,但对于接触过其他计算机语言的人来说学习起来还是比较轻松的。最后是Makefile,它相当于是一个管理文件,编译文件的工具,在后期我们使用大量文件的时候,用Makefile会很方便。
二.终端指令
2.1 Ubuntu软件的安装
在我们用Ubuntu编程的时候会用到各式各样的工具,所以我们需要知道Ubuntu如何在终端安装软件。
1.需要更新软件源,阿里源,清华源,163源等,这里使用的是清华源
清华源地址:清华源
找到自己Ubuntu的相关版本后复制其中的内容,然后进入Ubuntu,需要对原来的源进行备份,然后将清华源进行替换:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak //进行备份,需要管理员权限
vim /etc/apt/sources.list //打开资源文件,进行替换
sudo apt-get update //更换源后要进行更新,注意要联网
注意:如果更新不成功,将https中的s去掉
关于Ubuntu的网络配置可以看我的上一篇文章:Ubuntu的网络配置
2.操作
安装软件:
sudo apt-get install
卸载软件:
在平常的生活中,我们卸载软件的时候,如果只是单纯的卸载的话,它的数据其实会有保留的,当你把这个软件下载回来,会继承以前的数据。如果你不需要这些数据可以进行完全卸载的操作。如下:
sudo apt-get remove 软件名 //不完全卸载
sudo apt-get purge 软件名 //完全卸载
下载软件源码:
sudo apt-get source 软件名
这个操作需要更改sources.list中的内容,将注释的部分全部解除,然后注释掉原来的内容,然后回到终端再进行一次源更新,操作如上。
下载与清除软件安装包:
//下载软件安装包
sudo apt-get download 软件名
下载完安装包后,可以通过dpkg进行离线安装
//清除软件安装包
sudo apt-get clean
2.2 压缩与解压缩文件
2.2.1压缩文件
压缩文件也是我们经常要用到的一种指令,它分为3种压缩方式:gzip,bzip2,xz
gzip:
格式: gzip 文件名 ----->生成.gz后缀的文件
bzip2:
格式:bzip2 文件名 ----->生成.bz2后缀的文件
xz:
格式:xz 文件名 ----->生成.xz后缀的文件
在这三种压缩方式中,xz的效率最高,其次是bzip2,最后是gzip,所谓的效率是指压缩过后的文件的大小 xz < bzip2 < gzip,当然效率高的同时三种压缩方式所花费的时间也随之相反,xz > bzip2 > gzip。
2.2.2解压缩文件
三种压缩方式对应三种解压缩方式:gunzip、bunzip2、unxz。
格式:
gunzip 文件名
bunzip2 文件名
unxz 文件名
2.3 归档和拆包
上述三种压缩工具是针对文件的压缩,对于目录来说,上述三种方式是不能够进行压缩的。这里就要介绍一种目录压缩方式 tar ,它可以实现对目录的归档与拆包。
归档与归档并压缩:
tar -cf 目录名.tar 目录名 //归档,注意f必须要在最后
在归档的同时我们也可以对归档的文件进行压缩,用上述三种工具实现如下:
tar -czf 目录名.tar.gz 目录名 //用gzip工具进行压缩
tar -cjf 目录名.tar.bz2 目录名 //用bzip2工具进行压缩
tar -cJf 目录名.tar.xz 目录名 //用xz工具进行压缩
注意:使用的工具参数要与你书写的后缀名一一对应才行,不然进行拆包解压缩的时候会报错。
拆包与拆包并解压缩:
tar -xf 目标文件名
tar -xzf 目标文件名 //对.gz后缀的包进行拆包并解压缩
tar -xjf 目标文件名 //对.bz2后缀的包进行拆包并解压缩
tar -xJf 目标文件名 //对.xz后缀的包进行拆包并解压缩
注:tar -xf 是万能的拆包解压缩的指令,可以解压任何格式的压缩文件
2.4 文件的操作
2.4.1 cat
cat可以用来显示文件的内容,也可以用一些参数来进行文件的重定向与追加操作:
cat 文件名 //表示显示文件内容
cat 1.c > 2.c //将1.c的内容重定向到2.c中,如果2.c不存在就创建2.c并重定向。
cat 1.c >> 2.c //将1.c的内容追加到2.c中,如果2.c不存在就创建2.c并追加内容。
2.4.2 wc
wc是对文件的中单词数、字节数、行数的显示,不加参数的时候将会输出上述三种数据。
wc -w 文件名 //显示文件的单词数
wc -c 文件名 //显示文件的字节数
wc -l 文件名 //显示文件的行数
wc 文件名 //显示文件的单词数,字节数,行数
2.4.3 find
find是查找文件的指令,默认在当前目录下进行查找,如果需要在其他路径下查找,需要添加参数。
find 路径 -name 查找的内容
find test.c //在当前目录下进行查找
2.4.4 head和tail
head和tail是对单个文件的内容进行按行获取,head是从头开始,tail是从尾开始。
head 文件名 ----> 默认显示前十行
tail 文件名 -----> 默认显示文件的后10行
head -n 文件名 -----> 显示文件的前n行
tail -n 文件名 -----> 显示文件的后n行
head和tail指令,一般不会单独使用,例子:输出文件的第i行
2.4.5 管道符 |
管道符的作用是将上一条指令的输出,作为下一条指令的输入:
例如:管道符与head与tail连用,打印文件的后7行的第三行
tail -7 test.c | head -3 | tail -1
2.4.6 file
查看文件的详细信息
file 文件名
查看文件的详细信息
file a.out ----> ELF类型的,小端存储的可执行文件
2.4.7 查找字符串
grep 是用来查找一个文件中的字符串的,它有许多参数来进行配合:
grep "查找的字符串" 路径 参数
-n:回显行号
-R:递归搜索
-i:不区分大小写
-w:按单词查找grep "^helo" ./ -ni ---->开始为hello的字符串
grep "hello$" ./ -ni ----> 结尾为hello的字符串
2.4.8 剪切字符串
cut ,对于一个有规律的字符串,可以通过cut来进行剪切,如以":"分隔来选取你想要的字符串:
cut -d "分隔符" -f 域 字符串
练习:
把passwd中,ubuntu用户的用户名,描述信息,用户id,组id,组用户,家目录都截取出来。
grep "ubuntu" ./passwd | cut -d ":" -f 1,6
2.5 文件属性的操作
文件的类型:7种
b:块设备文件 c:字符设备文件 d:目录文件 -:普通文件 l:链接文件-->软链接文件
s:套接字文件 p:管道文件
2.5.1文件权限
r--4 w--2 x--1
可读 可写 可执行
当我们用ls -l 的时候会看到文件的属性,其中最前面的就是文件对于不同组的权限,用rwx来表示,从左到右依次是u(所属用户)g(所属组用户)o(其他用户)分别对应各自的权限。
改变文件的权限:
chmod 通过字母表示法
chmod 通过八进制数
八进制数0-7
例如:chmod 751 test.c
//将test.c 的权限修改为rwxr-x--x
2.5.2修改文件所属组
chgrp 组用户名 文件名
2.5.3修改文件所属用户
chown 用户名 文件名
chown ubuntu: test.c #把test.c的文件所属用户和文件所属组用户都改成ubuntu
chown ubuntu:root test.c #把test.c的文件所属用户改成Ubuntu,文件所属组用户改成root
chown :root test.c #把test.c的文件所属组用户改成root
chown root test.c #把文件所属用户改成root
2.5.4创建链接文件
硬链接:
创建了硬链接------> 可以理解为复制了一份,没有重新开辟内存,如果硬链接文件中的内容,修改了那么源文件的内容也会被修改,源文件和硬链接文件中的内容是保持一致的。
ln 绝对路径/文件名 绝对路径/连接名
软链接:
创建软连接------> 可以理解为快捷方式,如果源文件被删除,则链接断开,再重新创建一个同名的源文件,就会重新链接。
ln -s 绝对路径/文件名 绝对路径/链接名
当相对路径创建软连接时,如果源文件路径改变,链接文件就会失效。
2.6 用户的操作,增删改查
1.创建用户
sudo adduser 用户名 -----> 超级用户才能添加用户或者组
2.给用户添加sudo权限
sudo vim /etc/sudoers
进入文件后添加一行:
目标用户 ALL=(ALL) ALL
3.查看当前用户
whoami
4.查看用户的ID号
id 用户名
5.删除用户
sudo deluser 用户名 ------> 不会删除用户的家目录,rm -r /home/用户名
sudo userdel -r 用户名 ------>删除用户及用户的目录
6.修改用户的详细信息
修改用户信息的前提是,要修改的用户,在当前开机之后没有登陆过。
sudo usermod -c 999 ubuntu #修改用户的描述信息,修改的是开机时候的名字
sudo usermod -m -d /home/hello ubuntu
#把Ubuntu用户的家目录,改为/home/hello
sudo usermod -g 目标组 用户名
#修改用户的所属组为目标组
sudo usermod -l hello ubuntu
#修改用户Ubuntu的名字为hello
7.通配符
*:匹配一个或者多个字符
?:匹配一个字符
[]:1)[1-6]:匹配1-6当中的任一个字符
2)[1234]:匹配1,2,3,4中的任一个字符3)[1,2,3,4,f,t,c]:匹配当中任意一个字符
2.7 磁盘的操作
2.7.1查看u盘是否被Ubuntu识别
ls /dev/sd*
如果有结果说明识别成功
2.7.2磁盘分区
sudo fdisk /dev/sdb
m可以提示帮助信息,
d删除分区
n新建分区,每一步默认就可以
p打印分区
w:将操作写入磁盘并退出
q:直接退出
2.7.3挂载
sudo mount /dev/sdb1 ~/udisk
| |
要挂载的磁盘分区 挂载在Ubuntu中的位置/要保证位置存在
挂载成功后,对Ubuntu中目录的操作就相当于对磁盘的操作。
2.7.4取消挂载
sudo umount ~/umount -----> 直接加要取消的要挂载的位置
取消挂载后也不会影响之前存入的内容
2.7.5格式化
sudo mkfs.两次tab键可以显示出文件系统格式 /dev/sdb1
通常格式化为ext4/ntfs
三.shell编程
程序语言有编译型语言和解释型语言,shell属于解释型语言。对于大部分人来说shell是一门新的语言,但学过其他程序语言的话会很好上手。
1 shell使用的解析器
bash ----> Ubuntu用的解析器
sh -----> 开发板常用的
2 第一个shell脚本
#!/bin/bash
#写明解析器#打印hello world
echo "hello world"
第一行可以不写
3 执行脚本文件
1)修改文件权限
shell脚本默认是没有执行权限的,这时候可以通过chmod来进行权限的修改,就可以执行了
chmod 777 hello.sh #让hello.sh变成一个可执行的脚本文件
./hello.sh #执行脚本文件
2)bash 脚本文件
bash 是shell脚本所用的解析器
bash hello.sh #不需要修改文件权限
#其实是在后台打开了另一个终端,把结果返回到当前终端
3)source 脚本文件
source hello.sh #不需要修改文件权限
#当前终端解析指令,在当前终端返回
shell练习:
在脚本文件中完成以下操作: 1.在自己的用户主目录下新建两个子目录subdir1 subdir2 2.将/etc/passwd文件拷贝到subdir1,将/etc/group拷贝到subdir2 3.将subdir2重命名为subdir 4.对subdir1进行打包并且压缩成xz格式 5.将打包后的xz格式文件拷贝到subdir目录下 6.解压subdir目录下的压缩格式文件 7.查看subdir目录下所有的文件,在subdir目录下搜索ubuntu字符串并打印行号,不区分大小写
四.shell中的变量
4.1变量的定义和使用
1、在shell脚本中没有数据类型的说法,只有字符串
定义变量的格式:
变量名=变量的值
变量名='变量的值'
变量名="变量的值"
2、变量名的命名规范,和C语言中一致。
3、定义变量时等号的两侧都不能加空格
4、使用变量
$变量名
${变量名} (推荐使用)
5、使用已有的变量给新的变量赋值
#!/bin/bash
#定义变量
a=10 #定义了一个变量a值为10
b='2 3' #定义了一个变量b值为2 3
#c = 9 #error 等号两侧不能加空格
c=hello#打印变量
echo ${a}
echo ${b}
echo ${c}#使用已有的变量给新的变量赋值如果带空格需要使用""
#d=${c} world #error
d="${c} world"
#f='${c} i'
echo ${d}
#echo ${f} ${c} if=${c}
echo ${f}
4.2shell中的位置变量
$0 ----> $n
$1
$2
....
${10} ----> 9之后需要用{10}
位置变量获取的是终端输入的值,也有在之后for循环的时候用到。
4.3命令置换符
我们在shell脚本中是可以运行终端指令的,如果我们要将终端指令的值赋给一个变量,那么就需要命令置换符,而不是直接赋值。
把指令运行的结果赋给变量。
#!/bin/bash
a=`ls` #把ls的结果赋值给a
echo $a
b=$(ls ~) #把ls ~的结果赋值给b
echo $becho -n 输出的内容 ---->输出但是不换行
5.Linux中的环境变量
配置环境变量的作用是将一个文件夹放到可执行区域,然后就能直接执行该文件夹中的脚本
方法1:临时生效
export PATH=${PATH}:/home/ubuntu
| |
环境变量 追加的值
只对当前终端有效,退出终端就不生效了
方法2:修改/etc/bash.bashrc(常用)
在文件的最后添加语句
export PATH=${PATH}:/home/ubuntu 把Ubuntu添加到环境变量中
使配置生效
1、重启
2、source /etc/envirnment
特点:对所有用户都生效
方法3:修改家目录下的.bashrc 文件
在文件的最后添加语句
export PATH=${PATH}:/home/ubuntu把Ubuntu添加到环境变量中
使配置生效
1、重启
2、source /etc/envirnment
特点:只对当前用户生效
六.shell中的数组
6.1定义和初始化
数组名=(数组的值)
#初始化
数组名=(元素值 元素值 元素值 元素值 ····)
数组名=([下标]=元素值 [下标]=元素值 ····)
#第二种初始化方式,元素的个数就是具体值的个数
6.2访问数组中的元素
${arr[下标]} -----> 一定加{}
6.3使用数组中的某个元素
${arr[*]} -----> 全部元素
${arr[@]} -----> 全部元素#个数相关
${#arr[*]} -----> 全部元素的个数
${#arr[@]} -----> 全部元素
${#arr[0]} -----> 打印arr[0]元素的长度
6.4数组的拼接
数组名=(${arr[*] $brr[*]}) #本质也是数组初始化
七.shell中的输入输出
7.1输入--->read
read var1 ----> 从终端读入把值赋给var1
read -p "提示字符" 变量名 ------> 输出提示字符
read -t 5 var1 ---->控制时间,5秒内不输入内容自动结束read -n 3 var1 ----->控制位数,超过n位自动结束输入
read -s var3 ----->终端输入不显示,类似于输入密码
echo ${var3}
7.2输出--->echo
-
echo默认不解析转义字符加参数-e解析转义字符
-
-n取消换行
例:
#!/bin/bash
a=hello
#echo $a world --->hello world
echo '$a world' # '' 可以用在变量的值有空格并且只有赋值操作
echo "$a world"
b=hi
echo $a $b
echo '$a $b'
echo "$a $b\n" #echo默认不解析转义字符
echo '${a} ${b}' #变量不会被展开
echo -ne "${a} ${b}\n" #-e解析转义字符
八.shell中的算术运算
8.1 使用 (()) 进行算术运算----->常用于算术运算
1.格式: ((表达式)) 例:((2+6))
2.在(())中使用变量时,可以加$也可以不加(推荐加)
3.使用表达式的值,需要加$,变量名=$(())
4.运算符两边可以加空格,也可以不加空格。
5.((变量名=表达式)),可以直接在小括号内进行赋值操作
6.(())内,支持C语言的语法,可以完成复杂的运算
7.支持自增自减运算,不需要转义
#!/bin/bash
#(()),里面可以放多个表达式,取最后一个表达式的结果---逗号运算
sum=$((1+2,3+4,7+9))
echo $sumread -p "请输入:" a b
echo $((a+b))
echo $((a-b))
echo $((a*b)) #(())中*不需要转义
echo $((a/b))
8.2 使用$[]进行算术运算
1.格式:变量名=$[表达式1]
2.使用变量可以加$,也可以不加
3.运算符两边可以加空格,也可以不加空格。
4.必须使用变量接收表达式的结果 ----> $[]不能单独存在
#!/bin/bash
a=9
b=7
c=$[a + b]
#$[]不能单独存在,需要使用变量接收或者打印
#$[$a-$b]
echo $c
#echo $d
8.3 let进行算术运算
1.格式:let 变量名=表达式
2.运算符两侧不能加空格
3.使用变量的时候,可以加$也可以不加$
4.let必须放在表达式起始的位置
#!/bin/bash
a=9
b=7
let c=a+b
echo $c
8.4 使用expr 进行运算
8.4.1算术运算
-
格式:expr 表达式
-
运算符两侧必须加空格
-
使用变量时必须加$
-
expr不能进行,自增和自减运算
expr $a++ #error
expr $a + 1 #OK
-
在expr中使用某些字符的时候需要转义,\
-
如果想把expr的运算结果赋值给变量,需要使用命令置换符
-
expr可以执行更多的运算。
-
expr本质是一个指令
使用expr计算下面表达式的结果,并赋值给变量a
3*(2+1**4/3)
由于expr不支持幂运算,所以把1**4改成 1*4
#!/bin/bash
a=$(expr 3 \* \( 2 + 1 \* 4 / 3 \))echo $a
8.4.2关系运算
expr ARG1 | ARG2
如果arg1不为0/arg1和arg2同时为真,输出arg1的结果
如果arg2为真,arge为假,才输出arg2的结果
expr ARG1 & ARG2
如果两个都为真输出agr1的结果,如果有一个为假,结果为0
expr ARG1 < ARG2
expr ARG1 <= ARG2
expr ARG1 = ARG2
expr ARG1 != ARG2
expr ARG1 >= ARG2
expr ARG1 > ARG2
结果为0/1
#!/bin/bash
a=10
b=20echo -n "a | b ="
expr $a \| $b
echo -n "a & b ="
expr $a \& $b
echo -n "a < b ="
expr $a \< $b
echo -n "a > b ="
expr $a \> $b
echo -n "a != b ="
expr $a != $b
8.4.3 字符串运算
expr match 目标字符串 输入的字符串
#判断两个字符串,是否有一个可以作为另一个的子串,返回子串的长度,返回配完全成功的个数
注意:2个字符串是从头开始比较的,返回从头开始2个字符串有几个元素相同
str=hello
expr match "$str" "h"
expr substr 主字符串 开始位置 截取长度 #截取子串
#截取字符串,如果截取长度超过了字符串的长度,把字符串剩余的部分全部截除
expr substr "$str" 2 8
expr index 主字符串 查询的字符
#返回单个字符在字符串中第一次出现的下标
expr index "$str" "i"
expr length 字符串 #返回长度
expr length "hi"
练习:
通过read读入一个网址,将网址赋值给一个数组,如下: 使用expr以.为界限,截取字符串放入到数组中并输出 arr[0]=www arr[1]=baidu arr[2]=com 注:不能用cut
九.if判断语句
9.1定义
if [ 表达式 ] #单分支
then
shell语句
fi
if [ 表达式 ] #双分支
then
shell语句
else
shell语句
fi
if [ 表达式 ]
then
shell 语句
elif [ 表达式 ]
then
shell 语句
fi
注:[] 表示的是test 所以if判断也可以写成 if test 表达式,如果要用[] 则括号前后必须有空格
9.2 []或test后跟的参数
9.2.1对字符串:
-a :判断2个表达式是否同时为真
-o :任意一个表达式为真
-n :判断字符串不为空
-z :判断字符串为空
= :判断2个字符串是否相等
!= :判断2个字符串不相等
#!/bin/bash
#read str
#if [ -z $str ]
#then
# echo "str为空"
#else
# echo "str不为空"
#fi#判断字符串的大小关系,按位判断,遇到不同的就比较
str=hello
str1=hell
if [ "$str" == "$str1" ]
then
echo "str==str1"
else
echo "str!=str1"
fi
9.2.2对数据
-eq : 相等
-ge : 大于等于
-gt : 大于
-le : 小于等于
-lt : 小于
-ne : 不等于
#!/bin/bash
a=100
read b
#对于数据元素的操作
if test $b -ge $a
then
echo "b>=a"
if test $b -eq $a
then
echo "b=a"
else
echo "b>a"
fi
fi
9.2.3 对文件
对文件的操作只列举几个,具体可以通过man test 去查看详细信息
-b,是否为块设备文件
-L,是否为链接文件
-f,判断是否为普通文件
-r:判断文件是否存在,是否有可读权限
-w:判断文件是否存在,是否有可写权限
-x:判断文件是否存在,是否有可执行权限
9.3 case ....in 分支语句
case ${变量名} in
表达式1)
shell语句
;; ---->相当于switch···case里面的break,;;必须写出来
表达式2)
shell语句
;;
·····
*) ---->最后一种情况,不需要加;;
shell语句
esac表达式可以是常量
Y|yes|y|YES) ---->匹配四个字符串中的任一个
[1-6]) -----> 匹配1-6中的任一个字符
*} ---->相当于switch···case中的default,但是作为一个通配符,*放置的位置会影响执行的效果。
十.循环语句
10.1 while循环
while [ 表达式 ] ----> while [ 表达式 ] ; do
do
shell语句
donewhile true -----> while(1)
do
语句块
done
10.2 for循环
第一种:
for ((i=0;i<8;i++))
do
shell语句
done第二种:
for 变量名 in aa bb cc --->in后面是i可以出现的内容,控制循环次数
do
shell语句
doneseq队列 ----> 是一条指令,可以和for结合使用,一般放在in后面的位置
seq 起始值 间隔值 终止值for 变量名 `seq 1 1 10`
do
shell 语句
done
可以使用命令置换符,获取seq的结果。
10.3 select...in
1.格式:
#!/bin/bash
select var in aa bb cc dd
do
echo $var
done
#运行效果
ubuntu@ubuntu:day4$ bash sele.sh
1) aa
2) bb
3) cc
4) dd
#? --->提示输入
---->可以输入具体的数字,选择某一项
---->如果什么都不输入直接回车,再输出提示
2.与case...in结合使用
#!/bin/bash
select var in ubuntu windows macos redhat
do
case $var in
"ubuntu")
echo "使用的是Ubuntu系统"
;;
"windows")
echo "使用的是win系统"
;;
"macos")
echo "使用的是mac系统"
;;
"redhat")
echo "使用的是redhat系统"
;;
esac
done
10.4 beak/continue
1、break n ----->跳出n层循环
break 1 ----> 跳出一层循环,1可以不写
break 2 ----> 跳出两层循环2、continue n ----->跳出n层本次循环
continue 1 ----> 跳出一层本次循环,1可以不写
continue 2 ----> 跳出两层本次循环
continue跳出n层本次循环
十一.函数
格式:
function 函数名()
{
shell语句
}1>shell中定义函数时,可以不加function,也可以加function,推荐加
2>shell中没有参数列表,具体根据调用的格式决定
3>shell中函数没有返回值类型,具体根据函数内部书写格式决定
4>shell中仍然遵循,先定义后调用的规则,但是shell中,函数的定义和声明不能分开,只能把函数定义在函数调用的上方
5>shell函数中的变量,是全局变量
2.参数获取
shell中的函数是没有设定参数的,我们用$1.....$n来表示传入进函数的参数。
通过位置变量获取参数
$1····$n---->9以上${10}
$#传参个数
$* $@ 所有参数
3.返回值
用return返回值只能返回[0-255]之间的数,如果想要获取这之外的数就需要用一个变量去接收。函数的调用是函数名后接参数
只能返回[0-255]之间的数
sum=9
#readonly sum=9 #readonly修饰的变量不能被清空
#echo $sum
#unset sum
#echo $sum
#local a=8
#echo $a
#function add()
#{
# return $(($1+$2))
#}
#add $1 $2
add()
{
echo $(($1+$2))
}sum=`add 255 9` #只能返回[0-255]之间的数
#如果想计算,超过255的数,就不能返回结果
#可以使用echo打印,如果相接收,使用命令置换符接收a=`echo $sum`
echo $sum
十二.Makefile
Makefile是一个工程项目管理工具,对于大工程而言,我们会创建非常多的文件,这时候我们需要通过Makefile工具来进行文件管理。
1.准备工作
首先我们需要对编译有一个详细的了解:
-
编译过程
-
变量基础
-
目标和依赖的思想
预处理 编译 汇编 链接
gcc -E .c -o .i
gcc -S .i -o .s
gcc -c .s -o .o --->Makefile
gcc .o -o exe
2.Makefile的语法
Makefile由规则和依赖组成,其中也可以定义变量,规则的构成:目标 依赖 命令:
一个目标可以没有依赖,可以没有命令
一个目标也可以有多个依赖
一个规则必须有一个目标
Makefile把程序的执行分为两步
源文件----->汇编文件
汇编文件----->可执行文件
当文件发生修改,Makefile会根据,文件的时间戳判断文件是否更新,再决定是否重新编译。
初始版Makefile:
all:hello
hello:main.o hello.o
gcc main.o hello.o -o hello
main.o:main.c
gcc -c main.c -o mian.o
hello.o:hello.c
gcc -c hello.c -o hello.o
生成一个hello的可执行文件,hello通过 main.o 和 hello.o 编译生成,main.o由main.c编译生成,hello.o由hello.c编译生成。
3.Makefile中的变量
可以理解为宏定义,变量的概念赋值:
=:延迟赋值
+=:追加赋值
:=:直接赋值
?=:条件复制 ----->如果变量前面有值就不赋值
第二版Makefile:
我们运用到了上面提到的变量,将目标赋值给一个变量,依赖赋值给另一个变量,通过在Makefile中进行调用,可以减少我们的维护工作:
CC = gcc
EXE = hello
OBJS += main.o
OBJS += hello.o#引入%通配符
#自动变量$@,$^,$<是针对一条规则而言的#$@ 表示所有目标
#$^ 表示所有依赖
#$< 表示第一个依赖
#规则:目标、依赖和命令构成
all:${EXE}${EXE}:${OBJS}
${CC} $^ -o $@
main.o:main.c
${CC} -c $^ -o $@
hello.o:h.c
${CC} -c $^ -o $@
#会检查文件的时间戳
clean:
rm *.o ${EXE}
4.引入通配符
% ------> 目标和依赖之间的唯一匹配
最终版Makefile:
这里通过%来通配.o和.c文件,Makefile会根据你变量中的依赖进行一一对应:
CC = gcc
EXE = hello
OBJS += main.o
OBJS += hello.o#引入%通配符
#自动变量$@,$^,$<是针对一条规则而言的
#规则:目标、依赖和命令构成
all:${EXE}${EXE}:${OBJS}
${CC} $^ -o $@
%.o:%.c
${CC} -c $^ -o $@
#会检查文件的时间戳
clean:
rm *.o ${EXE}
十三.总结
这就是本次c高级部分的全部了,总的来说知识量还算挺多的,主要是shell编程的部分,还需要多加的练习,Makefile是我们C语言程序员必须要掌握的一项技能,以便于后期代码的维护。感谢阅读本文!