shell Makefile
[1] hexdump mouse0:查看当前用的那个鼠标
[2] 修改系统日期和时间
- 日期:
date -s "2021-04-27":修改日期为 2021-04-27 - 时间:
date -s 15:28:00:修改时间为 15:28:00 - 保存设置:
hwclock -w,保存设置
[3] 睡眠:sleep 1,睡眠一秒钟
[4] ldd命令,查看文件包涵了什么库的文件,ldd a.out
- 用
arm-none-linux-gnueabi-readelf -d ARM架构的可执行程序命令查看ARM架构的ELF文件所需要的库
[5] scp命令,例如将当前服务器的src.c文件拷贝到192.168.0.242用户名为ruan.kai的服务器上:scp ./src.c ruan.kai@192.168.0.242:/home/ruan.kai/
day1
列出目录内容 ls
- ls -i //查看文件的inode节点号(操作系统通过inode来区分文件)
- ls -lh //带单位的显示文件大小
软件包管理机制
软件包一般名称为rxvt_1%3a2.6.4-14_i386.deb
##一般都是
Filename_Version-reversion_Architecture.deb
软件名 软件版本 修订版本 体系架构
架构:
i386 ————> x86
amd64————> x64
-
离线安装:
dpkgsudo dpkg -i linuxqq…….deb ##安装linux ……qq sudo dpkg -l | grep qq #找到包含qq的软件包 sudo dpkg -s openssh #-s选项显示他向系统带来了哪些文件 sudo dpkg --remove linuxqq ##卸载qq for linux,可简写-r sudo dpkg -p 软件名 ##并删除配置信息 -
命令的本质,可执行程序
调试宏:
__FINE__ __func__ __FUNCTION__ __LINE__ -
在线安装:apt-get
sudo apt-get install 软件名 ##安装 sudo apt-get remove 软件名 ##卸载 sudo apt-get update ##更新源 sudo apt-get install -f ##更新依赖
文件的压缩和解压命令
gzip test ##压缩
gunzip test.gz ##解压缩 或者使用-d选项
gzip -d test.gz ##解压缩
gzip -l test.gz ##看压缩率
gzip -t test.gz ##测试文件完整性
bzip2 test ##压缩
bunzip2 test.bz2 #解压缩 也可以使用-l选项
xz test ##压缩
unxz test.xz ##解压缩 也可以用使用-l选项
####上面三种压缩方式速率从最快到最慢,效率从最差到最好,而且压缩之后原文件会消失,解压缩后,压缩文件会消失
##文件打包 tar
##如下代码, `tar` 用到了三个选项, `-c -v -f` ,`tar` 命令并不会删除原来的文件其中 `-c` 是指导 `tar` 创建归档文件
##`-x`是解打包,和 `-c` 对应
##`-v` 用于显示命令执行的过程,如果嫌 `tar` 显示太过繁琐,可以省略该选项
##`-f` 用于指定归档文件的文件名,简单来说就是重命名
## `-w`选项,每次将单个文件加入(或抽出)归档文件时都要询问用户
tar -cvf test.tar test/ #将test目录打包,命名为test.tar
tar -xf test.tar #解打包test.tar
tar -cvwf test.tar test/ #每个文件都要询问用户
tar -xvwf test.tar #每个文件都要询问用户
##`-z` 会自动调用 `gzip` 程序 -j会自动调用gzip2程序,-J会直接调用xz程序,效果一样
tar -czvf test.tar.gz test/ #相当于先打包test目录,然后用gzip压缩,等效下两条命令
tar -cvf test.tar test/
gzip test.tar
tar -xzf test.tar.gz #相当于先解压缩打包文件,然后再解打包,等效下两条命令
gunzip test.tar.gz
tar -xf test.tar
file命令
用于系统移植
预处理
gcc -E test.c -o test.i
编译
gcc -S test.i -o test.s
汇编
gcc -c test.s -o test.o
test.o :二进制文件
链接
gcc test.o -o test.elf
string.h里面的函数要学会自己实现,笔试重点考
day2
-
find命令
find ./ -name hello.c ##在当前目录寻找hallo.c文件,不知道具体名字可以用通配符 find /usr/bin/ -name zip -print ##查找名字里含有zip的文件然后打印出来 -
cat命令
cat -n test.c ##带行号显示修改配置文件 ~/.bashrc ,找到
56 'PS1='${debian_chroot:+($debian_chroot)}\u@\h:\W$ '
修改其中的W的大小写,最后使用
source ~/.bashrc立即生效 -
head和tail用于阅读文件开头和结尾
- head 后面加显示的行数,也可以加-n显示行号。
head -n -10 test1 - tail 显示结尾,和Head用法一样
- head 后面加显示的行数,也可以加-n显示行号。
-
more可以暂时显示一部分文件,回车键显示下一行,空格和f键显示下一页,b键显示前一页。q退出
-
less和more用法差不多,less -M 然后再接文件名,可以显示文件更详细的信息,当前页码,总页码和占总共百分比,
less -M test1
[2]linux内核
-
windows内核 = win内核 + gui + 动态库 + 盘符
-
ubuntu = linux内核(系统的调度,管理) + GUI(提供图像化页面的) + 动态库(接口) + 根文件系统(存储文件) + app
- 32位系统能够访问的内存空间:
- 0x0000_0000-0xFFFF_FFFF(2^32).应用层用03G,内核层用3G4G
- 64位系统能够访问的内存空间:
- x0000_0000_0000_0000 - 0xFFFF_FFFF_FFFF_FFFF(2^64)
linux内核分为这三层
应用层:app
- 命令
- 程序
- shell脚本
内核层:kernel
- linux内核5大功能
- 文件管理:文件的读写操作,
- 内存管理:虚拟地址到物理地址的映射
- 进程管理:多进程的调度,内核负责创建和销毁进程, 并处理它们与外部世界的联系(输入和输出),不同进程间通讯(通过信号,管道,或者进程间通讯原语)对整个系统功能来说是基本的,也由内核处理。 另外, 调度器, 控制进程如何共享CPU,是进程管理的一部分。更通常地,内核的进程管理活动实现了多个进程在一个单个或者几个CPU 之上的抽象。
- 网络管理:内核支持各种网络标准协议和网络设备。网络管理部分可分为网络协议栈和网络设备驱动程序。网络协议栈负责实现每种可能的网络传输协议(TCP/IP协议等);网络设备驱动程序负责与各种网络硬件设备或虚拟设备进行通信。
- 设备管理:Linux系统中几乎每个系统操作终都映射到一个或多个物理设备上。 除了处理器、内存等少数的硬件资源之外,任何一种设备控制操作都由设备特定的驱动代码来进行。内核中必须提供系统中可能要操作的每一种外设的驱动。
硬件层:hal
- 鼠标
- 键盘
- 硬盘
- 摄像头等
- 32位系统能够访问的内存空间:
[5]grep命令
-
grep命令:在文件中搜素某个字符串,格式为
grep 参数 “string" 目录\文件选项 效果 -n 加行号显示 -R 递归搜索目录下 -i 忽略搜索内容 -w 被匹配的内容只能是一个单词,而不能是单词某一个部分 "^string" 搜索开头是string的行 "string" 搜索结尾是string的行 "^string$" 整行是string的行 "^$" 空行
[6]cut对字符串进行截取
-
格式:
cut -d "分隔符" -f 编号 filename,比如在一个文件中有这样的一个字符串北京市:海淀区:西三旗越秀路:北京名园大学:华清远见 1 2 3 4 5
cut -d ":" -f 3 filename ##-> 截取3 cut -d ":" -f 1,3,5 filename ##-> 截取1,3,5 cut -d ":" -f 1-3,5 filename ##-> 截取1到3和5(1,2,3,5)注意:分隔符只能是英文字符
[7]管道 |
- 管道适用于传输信息的载体,管道一般用于两个命令
- 管道前边命令的输出结果作为管道命令后边的输入内容
[8]alias起别名
-
用法
alias 别名='命令'alias caiji='echo "caiji"'这个时候只是对当前终端临时有效,要想永久有效就要把这行代码放入.bashrc文件中,这个文件每次启动linux系统才会被执行,所以暂时不是生效,要想立即生效就要使用
source .bashirc命令,立即执行.bashrc文件
[9]开关机命令
-
定时关机
sudo shutdown -h now ##立即关机 sudo shutdown -h 15:30 ##15:30关机 sudo shutdown -h +60 ##60分钟之后关机 -
定时重启
sudo shutdown -r now ##立即重启 sudo shutdown -r 15:30 ##15:30重启 sudo shutdown -r +60 ##60分钟之后重启 -
取消定时关机和重启
sudo shutdown -c ##c代表clear -
立即重启
sudo reboot
[10] 通配符的使用
touch namea.c nameb.c namec.c nameA.c nameB.c nameC.c name1.c name2,c name3.c
ls *.c ## *匹配所有的字符串,匹配所有的.c文件
ls name?.c ## ?匹配一个字符
ls name[abcABCdfjlf].c ## []匹配一个字符,匹配括号里面其中的一个字符,所以会匹配abcABC六个.c文件
ls name[^a-c].c ##^符号是取反的意思,就是除了那些文件
ls name[^abc].c ##同上
ls name[a-c] ##也是匹配一个字符,但是这个字符是a到c之间的字符,是一个范围,他出来的结果和编码顺序有关,默认是zh_CN,出来五个文件aAbBc.c五个文件
##LANG=C:ABC...Zabc...z 先大写在小写,这时候只有a.c b.c c.c三个文件
##LANG=zh_CN:aAbBcC...zZ 大写小写一起的,这时候就有五个了
##默认是zh_CN编码顺序,要改变的话,需要用到export命令,来设置环境变量
export LC_ALL=C ##设置环境变量,变成第一种编码顺序,此时出来的是a.c b.c c.c文件
##想要回到默认的编码顺序,即第二种,用unset LC_ALL来取消设置的环境变量
unset LC_ALL ##取消环境变量的设置
-
特殊的通配符
[[:lower:]] ##小写字母 [[:upper:]] ##大写字母 [[:alpha:]] ##所有的字母 [[:digit:]] ##所有的数字 [[:alnum:]] ##所有的字母和数字
[11]echo命令
-
特殊的环境变量
- PATH:指定可执行文件(shell命令)的路径
- LD_LIBRARY_PATH:指定库的路径
echo "hello world!" ##打印 ##也可以用花括号,但是有的时候必须用花括号,例如有个变量str="lijia" echo "${str}caiji, lijiadacaiji." ##这样就可以输出lijiacaiji
[12]read命令
-
read 变量名1 变量名2 ……
read str1 str2 str3 ##然后输入三个字符串,其中不能用回车,用空格隔开 ##例如输入 lijia is caiji echo $str1 $str2 $str3 #输出 lijia is caiji -
带
-p选项的read命令可以先一段字符串,然后再赋值read -p "Please enter two string," str1 str2 ##先打印提示信息 -
带
-a选项的read命令是指读入数组read -p "Please enter > " -a arr ##-a选项是读入数组
[13]改变文件权限相关命令
chmod
一般格式sudo chmod u+r dir,
-
其中
u代表属主,可以用g(属组),o(其他人),a(所有人)代替 -
+号代表增加权限,还可以用-号(删除权限),=号(赋予文件权限),注意+ 和 =的区分 -
r代表读取权限,还可以用w(写入权限),x(执行文件) -
dir 代表要改变权限的文件
-
还可以直接用户 = 用户,把一个用户的权限都复制给另一个用户
sudo chmod a-x dir #给所有人(包括u、g、o)删除执行dir文件的权限 sudo chmod ug=rw, o=r dir #给u和g赋予读写权限,其他人赋给读取的权限 sudo o=u dir #把其他人的权限设置成和属主权限一样的 -
可以用八进制表示
| 八进制 | 二进制 | 权限 | 八进制 | 二进制 | 权限 |
|---|---|---|---|---|---|
| 0 | 000 | 无 | 4 | 100 | r-- |
| 1 | 001 | --x | 5 | 101 | r-x |
| 2 | 010 | -w- | 6 | 110 | rw- |
| 3 | 011 | -wx | 7 | 111 | rwx |
例如:权限rwxr-x--x,用八进制表示为751
sudo chmod 751 dir #属主属组其他人权限设置为rwxr-x--x
chown修改文件所属的用户或组
一般格式chown root:rk dir,其中root为属主,rk为属组,dir为要改变的文件
sudo chown root dir #只改变属主
sudo chown :root dir #只改变属组,冒号不可省略
sudo chown -R root:root die #改变dir文件和里面所有文件的属主
#属组,当然也可以只改变属组或者属
#主,省略规则和上面的一样
chgrp 修改文件所属的组
-
chgrp和chown 差不多,功能更少,只能改变属组
-
一般格式
chgrp root dir,其中root为属组,dir为要改变的文件
sudo chgrp root dir
sudo -R chgrp root dir #改变dir和dir文件里面所有文件的属组
输出重定向 >
- 一般shell指令都显示在用户显示器,大多数都在终端,用重定向可以改变输出位置
ls > ~/mydir/test1 #将当前目录ls显示结果打印到mydir/test1文件里面,如果mydir里面没有test文件,他会创建一个test文件,再写入结果,如果里面本来就有test文件而且里面还有内容,则会覆盖里面的内容,如不想覆盖的话用>>,在原来后面添加内容
ls ~mydir/ >> test2
输入重定向 <
- 一般的标准输入都是键盘,可以用 < + 文件,将文件内的内容作为输入
cat < ~/mydir/test #将test文件中的内容作为输入给cat,与cat ~/mydir/test 没区别
- << :明确告诉shell从键盘接受输入,一般格式为
cat << EOF,它说明让用户输入,当输入EOF时结束,然后把这些作为cat的输入,即他不会立即生效,它会等你输入结束才生效,当然EOF也可以改成其他的END,eof等正文不会出现的字符串
当然也可以组合使用输入输出重定向
cat << eof > test #接受键盘输入,把结果打印到test文件中
14[链接]
-
软连接
本质:给文件/目录创建一个快捷方式,需要
-s选项ln -s test.c test_1 #创建一个指向test.c文件的test-1软连接 ln -s ~/mydir local_mydir #创建一个指向路径的软连接- 对ubuntu下的压缩包,不要在windows下解压,因为windows不支持软链接,所以最后解压的文件里面的软链接都会丢失
- 软连接文件可以是文件也可以是目录
- 源文件删除后,软链接不可使用
- 移动软连接时候,如果是用的相对路径,那么转移后软链接基本就不可用了,但是如果用的是绝对路径创建的软链接,那么移动之后仍然可以使用,所以尽量使用绝对路径来创建软链接
-
硬链接
本质:给文件起了一个别名,是两个文件,但是他们实质上都指向一个文件,用不带任何选项的
ln来创建ln test.c test #建立test.c的硬链接test- 只能针对文件,不能对目录和路径创建硬链接
- 两个文件具有相同的内容,对其中一个文件更改会反映到另一个文件中
- 可以删除硬链接或者源文件,删除他们其中一个,文件仍然能够使用,但是两个都删除该文件就真正被删除了
[15]挂载相关的命令
linux文件系统一般有如下几个目录
- /bin目录,该目录下存放所有用户都可以使用的、基本的命令,这些命令在挂接其他文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。/bin目录下常用的命令有:
cat,chgrp,chmod,cp,ls,sh,kill,mount,umount,mkdir等 - /sbin目录,该目录下存放系统命令,即只有管理员能够使用的命令,系统命令还可以存放到/usr/sbin和/usr/local/sbin目录下,/sbin/目录中存放的是基本系统命令,他们用于启动系统,修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin、所以/sbin目录必须和根文件系统在同一个分区中。/sbin目录下常用的命令有:
shutdown,reboot,fdisk,fsck等,本地用户自己安装的系统命令放在/usr/local/sbin目录下 - /dev目录:该目录下存放的是设备文件,设备文件是linux中特有的文件类型,在linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件
- /etc目录:该目录下存放着各种配置文件,对于PC上的linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,他们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。
- /lib目录,该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。
- /home目录,用户目录,他是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件
- /root目录,根用户目录,与此对应,普通用户的目录是/home下的某个子目录
- /usr目录,该目录内容可以存放在另一个分区中,在系统启动后再挂接到根文件系统中的。/usr目录下,里面存放的是共享、制度的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的。其他主机相关的可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减
- /var目录,与/usr目录相反,/var目录中存放的是可变的数据。比如spool目录(mail,news),log文件,临时文件
- /proc目录,这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,他没有实际的存储设备,里面的目录,文件都是由内核临时生成的,用来标识系统的运行状态,也可以操作其中的文件控制系统
- /mnt目录,用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一个子目录,比如/mnt/cdram,/mnt/hda1。用来临时挂载光盘、硬盘
- /tmp目录,该目录用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以tmp目录必须存在并可以访问。
[16]挂载U盘
-
挂载
sudo mkdir /mnt/usb ## 首先先建立要挂载的U盘放置的文件夹 ##然后找到自己U盘所在的块,可以在/dev里面,里面可能有sd[a-z],一般sda是虚拟机自身的,后缀从b开始就是自己的U盘,假设是sbd1,当然也可以用df查看磁盘使用情况来找到sdb1 sudo mount /dev/sdb1 /mnt/usb ##挂载到usb文件中 ##然后就可以通过找到USB来访问U盘了 -
卸载(解挂载)
##首先应该退出挂载的目录,上述指的是usb目录,然后再执行解挂载命令 sudo umount /dev/sdb1 ##或者下述代码 sudo umount /mnt/usb ##卸载U盘文件系统 -
若是无法识别U盘,可能是协议不一样,USB2.0不兼容USB3.0,所以可以在
虚拟机--》设置--》USB控制器里面可以更改,3.0向下兼容2.0
[17]网络管理(windows和linux系统下)
- 见老师笔记里面的word文档
- ipconfig ping ifconfig 三个命令
day3
[1]ubuntu中系统环境变量的设置
-
env命令,打印ubuntu系统的所有的环境变量
-
PATH,系统环境变量
-
如何把自己可执行的文件当成一个系统命令运行
-
拷贝自己的可执行文件程序到PATH环境变量对应的路径下,使用如下命令
sudo cp hello /bin/ ##这样每次执行自己写的可执行文件不需要加./了,而且不管在那个路径都能使用这个命令 -
指定自己的可执行程序的路径到PATH变量中
-
设置环境变量临时有效
export PATH=/home/ruankai/mydir/mycommand/:$PATH ##export重新设置环境变量- 这种设置方式只对当前终端有效,一旦重启终端,export设置的环境变量将无效
-
修改配置文件永久有效
-
修改~/.bashrc 只对当前用户有效,打开该文件在最后添加上述代码,增加自己的路径
##在.bashrc文件最后加上如下代码 export PATH=/home/ruankai/mydir/mycommand/:$PATH -
此时他并不会立即生效,可以使用source命令让他立即生效,或者重启虚拟机也可以生效
-
-
-
-
修改/etc/bash.bashrc 对所有用户有效
- 打开文件最后添加上述代码,要立即生效也是用source命令或者重启
-
修改/etc/profile 对所有用户有效,具体操作和.bashrc文件一样
-
修改/etc/ecvironment 对所有用户有效
- 打开该文件后在PATH开头或者结尾加上自己路径:home/ruankai/mydir/mycommand- 同时也不会立即生效,用source或者重启虚拟机生效
【1】现在的电脑都是通过一个图形界面连接来打开一个应用程序,
进而实现对电脑硬件的控制(如:CPU,硬盘,内存)。
早期的电脑没有图形化界面,只能通过命令行的方式控制电脑。不管是图形化界面换是命令行的方式,都不可以直接控制电脑的底层硬件。而能够控制硬件的只有系统的内核可以完成,内核对于用户来说不可以直接操作。所以图形化的应用程序和命令行,是实现用户和内核交互的桥梁。
我们管命令行程序,在linux系统中叫做shell。
Shell 是一个应用程序,它连接了用户和 Linux 内核,
让用户能够更加高效、安全、低成本地使用 Linux 内核,
这就是 Shell 的本质。
【2】shell连接其他程序
shell本身支持的命令并不多,功能也有限,
但是shell可以调用其他应用程序,
一个应用程序本身就是一条命令,
这使得shell命令的数量无线扩展。
shell可以实现的功能也会非常强大。
shell内部的命令属于内置命令
shell外部的命令属于外置命令
【3】shell 编程 ---》脚本语言
shell并不是简单的堆砌命令,也可以使用shell进行编程
shell没有C语言强大,但是也支持基本的编程元素:例如
if...else 选择结构,case...in 开关语句,for、while、until 循环;
变量、数组、字符串、注释、加减乘除、逻辑运算等概念;
函数,包括用户自定义的函数和内置函数(例如 printf、export、eval 等)。
【4】语言分类
1》编译型语言: C语言 C++语言
需要有编译器: gcc g++
先编译--》运行
特点:
编译时间长,运行效率高。
用户不需要看到源码,安全系数比较高。
2》解释型语言 : shell python
必须有解析器: sh/bash python3.8
不需要编译,运行程序时解析器解析一条命令,
执行一条命令。
对于用户来说必须拿到脚本源码,
代码的安全性会降低。
【5】shell解析器的种类
Bourne Shell(简称sh)
Bourne Shell由AT&T贝尔实验室的
S.R.Bourne开发,也因开发者的姓名而得名。
它是Unix的第一个Shell程序,早已成为工业标准。
目前几乎所有的Linux系统都
支持它。不过Bourne Shell的作业控制功能薄弱,且不支持别名与历史记录等功
能。目前大多操作系统是将其作为应急Shell使用。
C Shell(简称csh)
C Shell由加利福尼亚大学伯克利分校开发。
最初开发的目的是改进Bourne Shell
的一些缺点,并使Shell脚本的编程风格类似于C语言,
因而受到广大C程序员的拥护。
不过C Shell的健壮性不如Bourne Shell
Korn Shell(简称ksh)
Korn Shell由David Korn开发,解决了Bourne Shell的用户交互问题,并克服
了C Shell的脚本编程怪癖的缺点。Korn Shell的缺点是需要许可证,这导致它应用
范围不如Bourne Shell广泛
Bourne Again Shell(简称bash)
Bourne Again Shell由AT&T贝尔实验室开发,
是Bourne Shell的增强版。随着几年的
不断完善,已经成为最流行的Shell。
它包括了早期的Bourne Shell和Korn Shell的
原始功能,以及某些C Shell脚本语言的特性。
此外,它还具有以下特点:能够提供
环境变量以配置用户Shell环境,
支持历史记录,内置算术功能,支持通配符表达式,
将常用命令内置简化。
sh===>csh==>ksh===>bash
bash兼容sh,所以一般情况下bash和sh并没有太大的区别
[6]第一个脚本文件
-
脚本文件后缀为sh
-
脚本文件的注释
-
单行注释:
# -
多行注释:
:<<! 多行注释处 !
-
-
一条shell命令占一行,不用分号结尾,一条shell命令如果无法写完,需要加""续行符
-
脚本文件第一行内容为
#!/bin/bash意思是使用bash解析器 -
执行shell脚本3种方式
./test.sh:要求脚本文件必须具有可执行权限,重新打开一个子终端下执行shell程序,将执行结果放回当前终端source ./test.sh:脚本文件不需要具有可执行权限,在当前终端下执行hsell脚本bash ./test.sh:脚本文件不需要具有可执行权限,重新打开一个子终端下执行shell程序,将执行结果放回当前终端
[7]shell变量的定义
-
variablename=变量值 variablename='变量值' variablename="变量值"- 等号两边不允许出现空格
- 如果变量值中没有空格,上面3中方式都一样,但是如果有空格,就需要单引号和双引号来设置边界
- 双引号会组织shell对大多数特殊字符进行解释。但"$"、"`"、"”"仍然保持其特殊含义。而单引号会组织shell对所有字符进行解释,不会对特殊字符进行解释,所以变量中要引用其他变量时,不能使用单引号
- shell变量名字的规则:和C语言一致
-
使用变量,注意用花括号和不用花括号的场合
-
unset 用来删除变量,但是不能删除只读变量,
unset 变量名 -
只读变量的设置,使用readonly关键字进行修饰
readonly str1="caiji" -
命令置换:倒引号``或者 $ ()
倒引号:当倒引号括起一个shell命令时,这个命令将会被执行,执行后的输出结果将作为这个表达式的值。倒引号中的特殊字符一般都被解释
-
练习:全程用shell指令在家目录创建一个hello.c文件,然后打印”hello world“
#! /bin/bash readonly homeDir=/home/ruankai/ readonly currentDir=`pwd` ##这里也可以用$(pwd) touch ${homeDir}hello.c echo "#include <stdio.h>" > ${currentDir}../../hello.c echo "int main(int argc, char * argv[])" >> ${homeDir}hello.c echo "{" >> ${homeDir}hello.c echo " puts(\"hello world!\");" >> ${homeDir}hello.c ##\为转义字符 echo " return 0;" >> ${homeDir}hello.c echo "}" >> ${homeDir}hello.c gcc ${homeDir}hello.c -o ${current}hello4 ./hello
[8]执行脚本文4件同时传递参数
-
类似于C语言中的argcv[0]、argv[1]、argv[2],shellli里面是$0、$1、$2……一直到无穷大
#! /bin/bash echo $0 echo $1 echo $2 echo $3##在终端里面输入 ./3.sh ruankai lijia fankewei ##他会依次打印, ##如果输入字符串中有空格就用 "" ./3.sh "ruan kai" "li jia" "fan ke wei" -
set后面接一个或者多个参数是,set 会把参数的值赋予位置参数,从 $1 开始赋值。如下例子:
$ cat test.sh #!/bin/bash echo $1 echo $2 echo $3 echo "---------------" ##上面会打印你命令行传递的参数 set first second third ##把参数改成这些几个字符 echo $3 $2 $1 $ ./test.sh ruan kai lijia ## 运行结果为 ## ruan ## kai ## lijia "-------------" ## third second first
[9]shell中特殊的变量
-
$0 :获取可执行文件的名字
-
$n:(n > 1)获取脚本文件执行传递的参数,
echo $1 $2 $3 -
$#:获取脚本文件执行传递的参数的个数,但是和C语言里面不一样,他不算argv[0],即,不计算$0
-
$*:获取脚本文件执行时传递的所有参数,相当于把$1、$2、$3……全部一起输出
-
$@:和$*一样
-
$?:获取上一条命令的退出状态,上一条命令成功退出状态就是0,否则为一个大于0的数
-
$$:获取当前shell进程的ID号
注:$*和$@的区别
-
在大多数情况下没有区别,但是加上双引号之后有区别
-
$*加上双引号之后,会看成一个整体的字符串处理
-
$@加上双引号后,会当成多个字符串处理,以空格为分隔符
#! /bin/bash function func() { echo $1 echo $2 echo $3 } echo $$ echo $# func $* func $@ func "$*" ##打印$1,他会把你输入的所有的字符串都看成一个字符给$1,另外两个没有就显示空行 func "$@" #
-
[10]ps命令
ps aux命令能够显示当前系统上运行的所有进程的信息(可以结合grep使用),查看状态ps -ef主要用于查看进程号ps lax可以提供父进程 ID(PID) 和谦让度( NI ),ps lax命令不会显示进程属主的用户名,因此运行速度更快
day4
[1]、shell中的数组
-
shell中只支持一维数组,不支持多维数组
-
数组的定义
(1)
arrayName=(元素0 元素1 元素2 ……)-
不需要指定数组元素的个数
-
元素之间使用空格隔开
-
所有元素使用()括起来,
arr=(11 22 33 44)数组长度为4 -
数组长度可变,
arr[5] = 66数组元素变成5,和C语言中并不一样 -
删除数组或者删除数组的某个元素
arr=([0]=aa [3]=cc [5]=dd) ##数组大小是3,注意是小括号,此时arr[1]里面没有值 arr=(11 22 hfjla) ##可以和字符串一起,因为实质上shell里面只有char类型的 echo "${arr[i]}" ##打印arr[i],即打印第i+1个元素 echo "${arr[*]}" ##访问数组中所有的元素,在一行中全部打印 echo "${arr[@]}" ##和上面一样 echo "${#arr[*]}" ##统计元素的个数 echo "${#arr[i]}" ##统计第i+1个元素里面的字符的数量 unset array ##删除整个数组 unset array[i] ##删除数组中的某个元素,删除后里面为空什么也不打印 -
下面是个例子
#! /bin/bash read -p "Please enter > " -a arr ##-a选项是读入数组 echo "\$arr[0] = ${arr[0]}" echo "\$arr[*] = ${arr[*]}" echo "\$arr[@] = ${arr[@]}" echo "\$#arr[*] = ${#arr[*]}" arr[10]=1010 echo "\${#arr[*]} = ${#arr[*]}" echo "\${#arr[2]} = ${#arr[2]}" // 数组下标为2的元素有多少个字符
-
[2]shell中的运算符

算术运算符 说明/含义
+、- 加法(或正号)、减法(或负号)
*、/、% 乘法、除法、取余(取模)
** 幂运算
++、-- 自增和自减,可以放在变量的前面也可以放在变量的后面
!、&&、|| 逻辑非(取反)、逻辑与(and)、逻辑或(or)
<、<=、>、>= 比较符号(小于、小于等于、大于、大于等于)
==、!=、= 比较符号(相等、不相等;对于字符串,= 也可以表示相当于)
<<、>> 向左移位、向右移位
~、|、 &、^ 按位取反、按位或、按位与、按位异或
=、+=、-=、
*=、/=、%= 赋值运算符,例如 a+=1 相当于 a=a+1,a-=1 相当于 a=a-1
- 由于shell中所有变量都是用字符串处理,如果直接使用运算符会当成字符串来处理,此时要达到运算的目的,必须使用shell中提供的特殊运算符
[3] (())运算
-
(())用于整数计算,不可以用小数或或者字符串,(())里面会解析表达式中引用的变量,所以变量可以省略$,当然也可以不用省略
((num1= 10 + 20)) ##给num1赋值 num2=$((10 + 20)) ##给num2赋值,空格可以省略 num3=$((10 + 20, 20 + 30, 30 + 40)) ##给num3赋最后一个值,该例为70 num5=$((num4 = 10 + 20,30 + 40)) ##给num5赋值为70,num4赋值为30 num5=$((num4 = 10 + 20)) echo $num1 echo $num2 echo $num3 echo $num4 echo $num5
[4] expr运算命令
-
expr运算命令 ----》 expr
-
expr是evaluate expressions的缩写,他是一个shell expr是一个功能强大,并且复杂的命令,他除了可以实现整数计算,还可以结合一些选项对字符串进行处理。例如计算字符串长度,字符串比较,字符串匹配,字符串提取
-
expr 表达式:expr运算符两边必须要加空格,括号也要有空格,因为他是一个命令,空格来区分参数的
-
对于特殊字符需要使用转义
* ##容易和通配符混淆,用\* 代替 () ##容易和数组混淆,用 \( \) 代替 <和> ##容易和重定向输入输出混淆,用 \< \> 代替 -
如下代码
num1=2 num2=3 expr $num1 + $num2 # 计算并显示 5 expr $num1+$num2 #2+3 expr $num1 \* $num2 #6 expr \( 10 + 10 \) \* 2 + 100 ##空格不可省略,输出140 status1=`expr $num1 \> $num2` ##2不大于3所以等于0 status2=`expr 3 \> 2` ##3大于2所以输出1 echo $status1 ##输出0 echo $status2 ##输出1
-
[5] expr对于字符串的运算
-
expr对于字符串的运算
-
expr match str1 str2,从str1开头进行匹配,匹配成功返回匹配字符的个数,一旦有一个匹配不上,就匹配失败,失败返回0#! /bin/bash str1="ruankailijia" expr match $str1 kai #0 从第一个字符开始匹配,开始不对就直接返回0 expr match $str1 ruan #4 返回成功匹配字符的个数 expr match $str1 rukai #0 匹配失败 -
expr substr str1 n1 n2,从str1的第n1个字符开始截取,连续截取n2个字符,此时字符计算和c里面不相同,从1开始,而不是从0开始#! /bin/bash str1="ruankailijia" expr substr $str1 5 3 #kai 从第5个字符开始截取,截取3个字符 -
expr index str1 str2,按照顺序遍历str1里面的字符,只要从str2中找到一个str1里面的字符直接结束,返回字符在str1的位置,全部都没找到返回0#! /bin/bash str1="ruankailijia" ##从r开始依次往后找,找到就返回r字符在str1中的位置,然后直接结束,停止寻找 expr index $str1 rfzcx #1 找到r expr index $str1 ruanfzxc #1 expr index $str1 kfzx #5 expr index $str1 kafzx #3 从r开始找没找到,然后开始找u也没找到,然后找a找到了,返回a在str1中的位置 expr index $str1 fczxi #7 expr index $str1 fxixyngg #4 expr index $str1 fxixynggu #2 expr index $str1 fxzy #0 全部都没找到,返回0 -
expr length str1计算str1中的字符的数量,即字符串的长度 -
$[ ]
$[]能对表达式求值。他会解析表达式中引用的变量,所以可以省略变量前面的$,而且对空格也没有要求,只能对整数进行运算#! /bin/bash num=5 num1=$[1+ 2 ] num1=$[3 * 2] num1=$[3 ** 2] num1=$[ num + num ] num1=$[ $num +$num ] echo $num1
-
[6] let 和 bc 运算命令
-
let:
-
let:let命令对空格要求不严格,而且他和(())一样会解析表达式中引用的变量,所以里面$可以省略,他和(())类似,只能对整数进行运算
#! /bin/bash num1=2 num2=3 ## let "var = 1 + 2" let "var+=1" let "var =$num1+ $num2" let "var = 2*3" let "var=2**3" let "var=8%3" echo $var
-
-
bc:
-
bc,有两种方式进入,首先可以直接在终端使用bc进入,最常用的方式是利用管道来进行计算
>>> bc ##从终端进入计算器 length() #用于求表达式的结果长度 length(300) 3 scale() #用于获取表达式小数点后位数 scale(3.14) 2 sqrt() #求平方根 scale=6;sqrt(21) #先要设置sqrt最少保留几位小数 4.582575 #如果使用了bc -l,可以将预置的数学运算导入 s(x) #sin函数,x为弧度 s(3.14) .00159265291648695254 c(x) #cos函数 c(0) 1.00000000000000000000 a(x) #arctang函数 a(sqrt(2)/2) 0.61547970867038734106 l(x) #自然对数 l(10) 2.30258509299404568401 e(x) #自然数为底的指数函数 e(2) 7.38905609893065022723 j(n,x) #Basel函数,n阶 j(1,3) 0.33905895852593645892
-
[7]if……else……
-
if语句,以
fi作为结束标志,继续执行后面的脚本if 条件 then 命令 else 命令2 fi ##或者 if 条件;then 命令 else 命令2 fi -
if……elif……
if 条件1 then 命令1 elif 条件2 then 命令2 …… else 命令 fi -
练习:根据输入的成绩,判断优、良、及格、差
##90-100 A ##70-90 B ##60-70 C ##0-60 D #! /bin/bash echo "Please enter score" read score if ((score \< 0 || score \> 100)) then echo "your enter error." elif ((score \>= 90)) then echo "A" elif ((score \>= 70)) then echo "B" elif ((score \>= 60)) then echo "C" else echo "D" fireturn 和 exit
#! /bin/bash num1=9 if ((num1 == 9)) then echo "Yes" #return 使用return 的时候只能用source命令执行,因为return返回一个值,但 他是在子终端中显示,执行完关闭了子终端,这时候会报错 exit ##这种就适用bash和./来执行,在子终端中exit退出终端 fi echo "----------------"
[8]test命令
-
test是一个shell的一个内置命令,主要用于判断条件是否成立,test命令常常和if和while语句结合使用
type 命令可以判断命令是否为内置命令还是外置命令- 一般返回结果包括buildin就为内置命令
-
test命令可以简写为[ ],这个时候的中括号里面可能要用转义 括号、大于小于。
test 1 + 2等价于[ 1 + 2 ],因为test是命令,所以空格不可省略 -
文件测试
选项 描述 test -b file或者[ -b file ]当file是块设备文件时返回为真 [ -c file ]当file时字符文件时返回为真 [ -d pathname ]当pathname是一个目录时返回为真 [ -e pathneme ]当 pathname 指定的文件或目录存在时返回真 [ -f file ]当 file 时常规文件(不包括符号链接、管道、目录等) 的时候返回真 [ -g pathname ]当 pathname 指定文件或者目录设置了SGID位时返回真 [ -h file ]或者[ -L file]当 file 是符号链接文件时返回真 [ -p file ]当 file 时命令管道时返回真 [ -r pathname ]当 pathname 指定的文件或目录设置了可读权限时返回真 [ -s file ]当 file 存在且大小位0时返回真 [ -u pathname ]当 pathname 指定的文件或目录设置了UID位时返回真 [ -w pathname ]当 pathname 指定的文件或者目录设置可写权限时返回真 [ -x pathname ]当 pathname 指定文件或目录设置了可执行文件权限时返回真 [ -o pathname]当 pathname 指定的文件或者目录被当前进程的用户拥有时返回真 [ -S filename ]当 filename 指定文件存在且位套接字文件时返回真 [ -k filename ]判断该文件是否存在,并且是否拥有SBIT权限 if [ -x /sbin/unconfigured.sh ] ##这里也可以写成 if test -x /sbin/unconfigured.sh then /sbin/unconfigured.sh fi ##如果文件/sbin/unconfigured.sh文件存在并且可以执行,就执行,否则就什么也不做 -
数值比较相关的test选项

# 根据输入的成绩,判断优、良、及格、差 #!/bin/bash read -p "Please enter score > " score if [ $score -lt 0 -o $score -gt 100 ] then echo "your enter error." elif [ $score -ge 90 ] then echo "A" return 100 elif [ $score -ge 80 ] then echo "b" elif [ $score -ge 70 ] then echo "c" elif [ $score -ge 60 ] then echo "d" else echo "e" fi -
复合表达式
操作符 描述 ! expr "非" expr2 -a expr2 "与" expr1 -o expr2 "或" #! /bin/bash echo "Please enter password:" read password echo "Please enter pass:" read pass if [ -z "$password" -o -z "$pass" ] then echo "Please enter the password and pass." else echo "Yes." fi- 上面代码,如果两次都没输入东西,那么就执行then,否则执行else
- shell中
&& 和 ||可以替代test 和 []命令内建的"-a" 和 " -o" ,上面if语句里面也可以这样写:if [ -z "$password" ] || [ -z "$pass" ]
-
与字符串判断相关的test选项
选项 描述 [ -z str ]当字符串str长度为0时返回真 [ -n str ]当字符串str长度大于0时返回真 [ str1 = str2 ]当字符串str1和str2相等时返回真 [ str1 != str2 ]当字符串str1和str2不相等时返回真 [ str1 \> str2 ]判断str是否大于str2 [ str1 \< str2 ]判断str是否小于str2 -
与逻辑运算符相关的test选项
注意:如果[]命令中引用其他的变量,建议引用变量是加上""
[7] case...in 语句
一、语法
case expression in
pattern1)
命令1
;;
pattern2)
命令2
;; ##相当于break
……
*) ##相当于default
命令
;;
esac
二、注意事项
-
最后一个分支的两个分号可以省略,但是其他分支中的分号都不能省略
-
exprssion 可以是变量、表达式、数字、字符串
-
pattern 可以是数字、字符、字符串、正则表达式
-
正则表达式(真值表达式)
* ---> 匹配所有的字符 [abc] --> abc中的任意一个 [a-c] --> a-c之间的任意一个 [a-zA-Z] -->所有的英文字符包括大小写 [1-9] --> 数字1-9之间的任意一个 "abc" | "ABC" --> 字符串adc或ABC任意一个
三、练习题
-
成绩分类使用case...in实现
#! /bin/bash read -p "Please enter your score > " score if [ $score -gt 100 ] || [ $score -lt 0 ] then echo "enter error." exit fi expr $score / 10 case `expr $score / 10` in 10) echo "full mark." ;; 9) echo "A" ;; [78]) ##这里如果有10这个选项,可以这样实现 [78] | 10 ) echo "B" ;; 6) echo "C" ;; [0-5]) echo "D" ;; esac
[8] while循环
一、格式
while test commands #首先检查test commands是否为真(0) 也可以用[]来判定条件
do
commands #为真执行commands,再次检查
done
二、如下代码就是从1一直加到100的和
#! /bin/bash
i=1
sum=0
while [ "$i" -le 100 ]
do
sum=`expr $sum + $i`
i=$[$i + 1]
done
echo "$sum"
三、死循环
while true
do
command
done
[9] for 循环
一、第一种
for (( 表达式1;表达式2;表达式3 ))
do
表达式4
done
##和c语言的执行顺序一样
二、计算1到100的和
#! /bin/bash
sum=0
for (( num=0; num <= 100; num++ ))
do
sum=$[ $sum + $num ]
done
echo "$sum"
三、第二种方式的for循环
-
格式
for index in list ##index为变量时不需要用$符号 do command done -
执行过程:从list中按顺序依次从左到右取值,赋值给变量index,直到所有的值赋值结束,退出for循环,list有下面几种重要形式
-
list可以直接给具体的值,每个值之间用空格隔开
for i in 10 20 30 40 a b
-
list 可以是一个区间值
for i in {0..100}从0到100,也可以倒序列表{100..0}
-
list 可以时特殊的变量
for i in $*for i in $nfor i in ${arr[*]}
-
list 可以使用通配符
* ?
-
例子
#! /bin/bash for i in 1 2 3 4 5 do echo "$i" echo "hello" done echo "----------" for i in {1..10} ##循环10次 do echo "$i" echo "hello" done echo "------------" arr=(1 2 a v c 3 4) ##遍历数组各个元素 for i in ${arr[*]} do echo $i done echo "--------" num=0 for filename in /home/linux/mydir/c/*.c do echo "$filename" ##打印每个.c文件 num=$[ num + 1 ] done echo "$num"
-
[10]day4作业:实现实时时钟功能,每一秒打印一次时间
#! /bin/bash
year=2020
month=10
week=4 ## 星期几
day=16
hour=0
min=0
sec=0
while true
do
sec=$[ $sec + 1 ]
if [ $sec -eq 60 ];then
sec=0
min=$[ $min + 1]
if [ $min -eq 60 ];then
min=0
hour=$[ $hour + 1 ]
if [ $hour -eq 24 ];then
hour=0
day=$[ $day + 1 ]
week=$[ "$week" + 1 ]
if [ $week -ge 8 ];then
week=1
fi
case $month in
[13578] | 10 | 12 )
if [ $day -gt 31];then
day=1
month=$[ $month + 1 ]
fi
;;
[469] | 11 )
if [$day -gt 30];then
day=1
month=$[ $month + 1 ]
fi
;;
2)
if [ \( `expr $year % 4` -eq 0 \) -a \( `expr $year % 100` -ne 0 \) ] || [ `expr $year % 400` -eq 0 ];then
if [ $day -eq 30 ];then
day=1
month=$[ $month + 1 ]
fi
elif [ $day -eq 29 ];then
day=1
month=$[ $month + 1 ]
fi
;;
esac
if [ $month -ge 13 ];then
month=1
year=$[ $year + 1 ]
fi
fi
fi
fi
sleep 1
##下面这种方式输出也可以
#echo "$year"-"$month"-"$day" "$week" "$hour":"$min":"$sec"
#echo "${year}-${month}-${day} ${week} ${hour}:${min}:${sec}"
#printf "%d-%d-%d %d %d:%d:%d\n" "$year" "$month" "$day" "$week" "$hour" "$min" "$sec"
##这种输出有问题,59秒之后就是0秒,所以0会覆盖5但是9不会被覆盖,直到0秒到了10秒就刷新
#printf "%d-%d-%d %d %d:%d:%d\r" "$year" "$month" "$day" "$week" "$hour" "$min" "$sec"
printf "%02d-%02d-%02d %02d %02d:%02d:%02d\r" "$year" "$month" "$day" "$week" "$hour" "$min" "$sec"
done
day5
[1] select……in……语句
一、select...in 主要用来提高代码的交互性,通过菜单选项的方式,选择要执行的shell命令或者程序,
二、语法如下
##list就是菜单列表,可以通过菜单选择一个值,然后select会赋值给choice
select choice in list
do
shell语句
done
三、select...in 是一个死循环,即使你输入空值或者错误的值他也不会退出循环,除非遇到break或者输入ctrl + D 才会退出select...in ,执行下一条命令
四、实现计算器的功能
#! /bin/bash
select choice in "add" "sub" "mul" "div"
do
case $choice in
"add")
read -p "Please input a expression > " -a arr
./add ${arr[*]} ##把数组中的字符串元素作为add可执行文件的命令行参数,并执行add文件
;;
esac
done
echo "Done!"
//add.c文件,先编译生成可执行文件add才能运行上面的文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
float num1, num2;
if (argc != 4)
{
puts("enter expression error.");
return;
}
if (strcmp(argv[2], "+"))
{
puts("enter operator error.");
return;
}
num1 = atof(argv[1]);
num2 = atof(argv[3]);
printf("%s %s %s = %f\n", \
argv[1], argv[2], argv[3], num1 + num2);
return 0;
}
[3] break 和 continue
- break:语法格式,
break num,其中num的值要大于1,等于1的时候可以省略,它的作用是跳出 num 层循环 - continue:语法格式,
continue num,其中num的值要大于1,等于1的时候可以省略,它的作用是跳出 当前的num 层循环,执行下一次的循环
[4] shell 中的函数
一、语法格式
function functionName()
{
函数体
返回值
}
二、注意
-
函数没有形参,调用函数时,传递实参就有参数,不传递参数就无参数
-
函数不需要指定返回类型
-
函数需要 function 关键字修饰
-
函数内部获取传递的参数的方式
- $1 $2 $3……
- $* 所有参数
- $@ 所有参数
-
函数调用方式
-
函数没有参数,没有返回值
funname -
函数有形参,没有返回值
functionName 参数1 参数2... -
函数有返回值
sum=$(functionNama 参数1 参数2 参数3...)
-
-
shell中所有的变量都是全局变量,即时在函数体内部定义的变量依然是一个全局变量
-
如果想在函数中的变量定义为局部变量需要使用 local 关键字修饰
-
$? 获取上一条指令的执行的状态,上一条命令执行成功返回0,否则返回大于0的数。指令执行的返回值范围是0-255,如果函数返回值大于255,通过echo返回,或者定义变量为全局变量
#! /bin/bash function func() { local variable=513 return $variable } func echo $? ##返回func中返回的值variable,当该变量大于255时 $?就从0开始重新到255,一直循环 function func_status() { echo "world -----" dsfjalkjfl echo $? return $? } if func_status then echo "1" else echo "0" fi
Makefile
-
makefile:是用于项目工程管理的文本文件
-
make:make 是一个可执行程序,是ubuntu系统中的一个命令,make 可执行程序在 /usr/bin目录下,当我们在终端中执行make命令时,make 命令会在当前目录下找到 Makefile 文件,对 Makefile 文件进行解析,并且执行 Makefile 中的语句,完成对工程的编译
-
Makefile 用途:Makefile 中主要用于工程文件编译,Makefile中写的是编译工程的一些命令,当你执行 make 时,会执行 Makefile 中的命令,完成对整个工程的编译,最终生成一个可执行文件
-
要掌握程序的编译流程
##预处理 编译 汇编 链接 gcc -E *.c -o *.i gcc -S *.i -o *.s gcc -c *.s -o *.o gcc *.o -o *.elf -
要具有一定面向依赖的思想
## *.i 依赖 *.c ## *.s 依赖 *.i ## *.o 依赖 *.s ## *.elf 依赖 *.o
[1]Makefile 中的语法规则
-
语法规则的构成
目标:目标的依赖 (tab)shell命令 -
注意
- 目标必须顶格写
- shell命令前边必须是一个tab键,不能用4个空格代替
- Makefile 文件中可以包含多个规则,至少要有一个规则
- 一个规则中可以没有目标的依赖
- 一个规则中可以没有shell命令
- 命令前面加@,可以取消命令的回显
-
执行make命令
- 如果后面没有接目标,那Makefile会默认找到Makefile 文件的第一个目标,执行第一个目标后边的命令
- 如果 make 后面加上相应的目标,那么会执行对应目标后边的命令
make -jn 目标开辟n个线程执行Makefile文件,对工程进行编译,提高编译的效率
-
案例
all:hello.c ##不带目标的make 或者带 all 目标的make命令都会执行第一个 @gcc hello.c -o hello ##命令前面加@,可以取消命令的回显 clean: ##可以没有依赖 rm hello ruankai:lijia ##可以没有命令,此时找不到lijia,他会先解析其他规则,后面会生成lijia这个目录 lijia1:hello.c gcc hello.c -o hello lijia:all ##可以没有命令 -
实际项目,假如我有一个相加函数,相减函数,和一个自己定义的头文件,然后就是一个主函数
//主函数 #include <stdio.h> #include "custom.h" int main(void) { printf("add = %d\n", add(5, 5)); printf("sub = %d\n", sub(5, 5)); return 0; }此时我的makefile 可以这么写
##当我执行make all,或者直接执行make时,按照关系树给我一条条执行 all:out out:add.o sub.o test.o gcc add.o sub.o test.o -o cal ##开始链接 add.o:add.c gcc -c add.c -o add.o ##gcc -c是只编译,不链接 sub.o:sub.c gcc -c sub.c -o sub.o test.o:test.c gcc -c test.c -o test.o clearall: rm add.o test.o sub.o cal clear: rm add.o sub.o test.o -
Makefile 执行过程
- 首先进入编译的工程目录,执行make 命令
- 他会自动找到工程目录下的Makefile 文件,进行依赖关系解析,生成依赖关系树
- 命令执行阶段。他会把解析的依赖关系树加载到内存,安装依赖关系,按照顺序执行命令,声称对应的文件
-
注意
- 如果已经执行过依次 make 编译且没有删除相关文件的话,此时再次执行make 进行编译时,make会检查文件的时间戳,判断生成的文件时间是否早于对应源文件
- 如果比源文件生成的早,就证明源文件进行过修改,执行 make 时不会重新编译源文件,使用之前生成的生成文件,这样的目的主要是为了缩短编译的时间
[2] Makefile 中的变量
-
变量的定义
- variable = value,使用 variable 变量时,才将value 赋值给variable ,延时赋值(赋值)
- variable := value,立即赋值
- variable ?= value,判断 variable 是否有初始值,如果有初始值,就不对变量进行赋值,如果没有初始值,就对变量进行赋值
- variable += value,追加赋值,variable 中原有的值和 value 的值之间用空格隔开
-
变量的使用
- $(variable) 注意和shell中变量使用的区别
-
Makefile 中特殊的变量
-
$@ 目标
-
$< 第一个依赖
-
$^ 所有的依赖
-
$? 所有时间戳比目标文件晚的依赖文件,并以空格分开
-
*匹配字符串 -
*.c当前目录下所有的.c文件 -
% 模式匹配,目标和依赖之间的匹配,比如
%.c表示当前目录下的某一个.c文件 -
案例
name = "ruan kai" name1 := "li jia" name2 ?= "da cai ji" ##name2 里卖没有初始值,所以此时能够赋值 name ?= "fan ke wei" ##此时name中已经有值了,所以不能赋值 name2 ?= "xiao cai ji" ##不能继续赋值 name3 ?= "xing" ##第一次赋值,可以赋值成功 name1 += $(name2) ##追加,li jia da cai ji all: @echo "$(name)" ##如果不想让命令回显,使用@ @echo "$(name1)" @echo "$(name2)" @echo "$(name3)"
-
-
简写makefile
Target := out CC := gcc OPC := -c OBJ := add.o OBJ += sub.o OBJ += test.o all:$(Target) out:$(OBJ) $(CC) $^ -o $@ %.o:%.c ##任意一个.o文件:任意一个.c文件 $(CC) $(OPC) $< -o $@ clearall: rm *.o $(Target) clear: rm *.o -
函数
-
模式替换函数-- patsubst
$(patsubst pattern, replacement, text),他会搜索 text 中以空格分开的单词,将不符合模式的 pattern 替换为 replacement- 例如
$(patsubst %.c, %.o, 1.c 2.c a.c),该函数的作用是把字符 ”1.c 2.c a.c“结尾的单词替换成以 .o 结尾的字符,函数返回结果为 "1.o 2.o a.o"
-
获取匹配模式文件名的函数 -- wildcard
$(wildcard PATTERN),理出当前目录下所有符合模式 ”pattern“ 的文件名。- ”pattern“ 可以使用 shell 中可识别的通配符,包括 ? * 等
- 例如
$(wildcard *.c)返回值为当前目录下所有的 .c 源文件列表
-
所以上述的makefile 还可继续简写,变成一个万能的makefile
Target = out CC = gcc OPC = -c ##OBJ = test.o sub.o add.o OBJc := $(wildcard *.c) ##列出出当前目录中所有的.c文件 OBJo := $(patsubst %.c, %.o, $(OBJc)) ##把OBJc中所有的.c文件替换成.o文件 all:out out:$(OBJo) $(CC) $^ -o $@ %.o:%.c $(CC) $(OPC) $< -o $@ -I./head ##-I是指定头文件路径 clearall: rm *.o out clear: rm *.o -
如果我们想把自己定义的头文件放在head 目录下,只需要在
gcc -c *.c -o *.o -I./head/加上 -I 选项 -
如果我们把自己定义的函数.c文件统一放到一个目录中,例如 src 目录中,只需要在文件前加上路径,还可以用函数把生成的 .o 文件也统一放到一个目录中
Target = out CC = gcc OPC = -c ##OBJ = test.o sub.o add.o srcDir:=./src OBJc := $(wildcard $(srcDir)/*.c ./*.c) OBJo := $(patsubst %.c, %.o, $(OBJc)) ##把OBJc中所有的.c文件替换成.o文件 all:out @echo $(OBJc) @echo $(OBJo) out:$(OBJo) $(CC) $^ -o $@ %.o:%.c $(CC) $(OPC) $< -o $@ -I ./head clearall: rm $(srcDir)/*.o ./out clear: rm $(srcDir)/*.o
-
-
把 .c .o 文件分别放在自己的目录中
- 加前缀函数 -- addprefix
$(addprefix prefix, names1...),该函数会将 name 中的每一个文件添加前缀 prefix ,参数 name1 是以空格分割的文件名序列- 返回值:以单空格分隔的添加了前缀 prefix 的文件名序列
- 示例:
$(addprefix src/, foo bar),返回值为src/foo src/bar

浙公网安备 33010602011771号