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
  1. 离线安装:dpkg
    sudo 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 软件名   ##并删除配置信息
    
  2. 命令的本质,可执行程序

    调试宏:

    __FINE__
    __func__   __FUNCTION__
    __LINE__
    
  3. 在线安装: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

  1. find命令

    find ./ -name hello.c   ##在当前目录寻找hallo.c文件,不知道具体名字可以用通配符
    find /usr/bin/ -name zip -print  ##查找名字里含有zip的文件然后打印出来
    
  2. cat命令

    cat -n test.c  ##带行号显示
    

    修改配置文件 ~/.bashrc ,找到

    56 'PS1='${debian_chroot:+($debian_chroot)}\u@\h:\W$ '

    修改其中的W的大小写,最后使用 source ~/.bashrc 立即生效

  3. head和tail用于阅读文件开头和结尾

    • head 后面加显示的行数,也可以加-n显示行号。head -n -10 test1
    • tail 显示结尾,和Head用法一样
  4. more可以暂时显示一部分文件,回车键显示下一行,空格和f键显示下一页,b键显示前一页。q退出

  5. less和more用法差不多,less -M 然后再接文件名,可以显示文件更详细的信息,当前页码,总页码和占总共百分比,less -M test1

[2]linux内核
  1. windows内核 = win内核 + gui + 动态库 + 盘符

  2. 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

    • 鼠标
    • 键盘
    • 硬盘
    • 摄像头等
[5]grep命令
  1. grep命令:在文件中搜素某个字符串,格式为grep 参数 “string" 目录\文件

    选项 效果
    -n 加行号显示
    -R 递归搜索目录下
    -i 忽略搜索内容
    -w 被匹配的内容只能是一个单词,而不能是单词某一个部分
    "^string" 搜索开头是string的行
    "string" 搜索结尾是string的行
    "^string$" 整行是string的行
    "^$" 空行
[6]cut对字符串进行截取
  1. 格式: 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]管道 |
  1. 管道适用于传输信息的载体,管道一般用于两个命令
  2. 管道前边命令的输出结果作为管道命令后边的输入内容
[8]alias起别名
  1. 用法 alias 别名='命令'

    alias caiji='echo "caiji"'
    

    这个时候只是对当前终端临时有效,要想永久有效就要把这行代码放入.bashrc文件中,这个文件每次启动linux系统才会被执行,所以暂时不是生效,要想立即生效就要使用 source .bashirc 命令,立即执行.bashrc文件

[9]开关机命令
  1. 定时关机

    sudo shutdown -h now  ##立即关机
    sudo shutdown -h 15:30  ##15:30关机
    sudo shutdown -h +60   ##60分钟之后关机
    
  2. 定时重启

    sudo shutdown -r now   ##立即重启
    sudo shutdown -r 15:30  ##15:30重启
    sudo shutdown -r +60  ##60分钟之后重启
    
  3. 取消定时关机和重启

    sudo shutdown -c  ##c代表clear
    
  4. 立即重启

    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  ##取消环境变量的设置
  1. 特殊的通配符

    [[:lower:]] ##小写字母
    [[:upper:]]  ##大写字母  
    [[:alpha:]]  ##所有的字母
    [[:digit:]]  ##所有的数字
    [[:alnum:]]  ##所有的字母和数字
    
[11]echo命令
  1. 特殊的环境变量

    • PATH:指定可执行文件(shell命令)的路径
    • LD_LIBRARY_PATH:指定库的路径
    echo "hello world!"  ##打印
    ##也可以用花括号,但是有的时候必须用花括号,例如有个变量str="lijia"
    echo "${str}caiji, lijiadacaiji."  ##这样就可以输出lijiacaiji
    
[12]read命令
  1. read 变量名1 变量名2 ……

    read str1 str2 str3   ##然后输入三个字符串,其中不能用回车,用空格隔开
    ##例如输入 lijia is caiji
    echo $str1 $str2 $str3  #输出 lijia is caiji
    
  2. -p 选项的 read 命令可以先一段字符串,然后再赋值

    read -p "Please enter two string," str1 str2  ##先打印提示信息
    
  3. -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 修改文件所属的组
  1. chgrp和chown 差不多,功能更少,只能改变属组

  2. 一般格式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[链接]
  1. 软连接

    本质:给文件/目录创建一个快捷方式,需要 -s选项

    ln -s test.c test_1   #创建一个指向test.c文件的test-1软连接
    ln -s ~/mydir local_mydir  #创建一个指向路径的软连接
    
    • 对ubuntu下的压缩包,不要在windows下解压,因为windows不支持软链接,所以最后解压的文件里面的软链接都会丢失
    • 软连接文件可以是文件也可以是目录
    • 源文件删除后,软链接不可使用
    • 移动软连接时候,如果是用的相对路径,那么转移后软链接基本就不可用了,但是如果用的是绝对路径创建的软链接,那么移动之后仍然可以使用,所以尽量使用绝对路径来创建软链接
  2. 硬链接

    本质:给文件起了一个别名,是两个文件,但是他们实质上都指向一个文件,用不带任何选项的 ln来创建

    ln test.c test    #建立test.c的硬链接test
    
    • 只能针对文件,不能对目录和路径创建硬链接
    • 两个文件具有相同的内容,对其中一个文件更改会反映到另一个文件中
    • 可以删除硬链接或者源文件,删除他们其中一个,文件仍然能够使用,但是两个都删除该文件就真正被删除了
[15]挂载相关的命令
linux文件系统一般有如下几个目录
  1. /bin目录,该目录下存放所有用户都可以使用的、基本的命令,这些命令在挂接其他文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。/bin目录下常用的命令有:cat,chgrp,chmod,cp,ls,sh,kill,mount,umount,mkdir
  2. /sbin目录,该目录下存放系统命令,即只有管理员能够使用的命令,系统命令还可以存放到/usr/sbin和/usr/local/sbin目录下,/sbin/目录中存放的是基本系统命令,他们用于启动系统,修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin、所以/sbin目录必须和根文件系统在同一个分区中。/sbin目录下常用的命令有:shutdown,reboot,fdisk,fsck等,本地用户自己安装的系统命令放在/usr/local/sbin目录下
  3. /dev目录:该目录下存放的是设备文件,设备文件是linux中特有的文件类型,在linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件
  4. /etc目录:该目录下存放着各种配置文件,对于PC上的linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,他们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。
  5. /lib目录,该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。
  6. /home目录,用户目录,他是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件
  7. /root目录,根用户目录,与此对应,普通用户的目录是/home下的某个子目录
  8. /usr目录,该目录内容可以存放在另一个分区中,在系统启动后再挂接到根文件系统中的。/usr目录下,里面存放的是共享、制度的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的。其他主机相关的可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减
  9. /var目录,与/usr目录相反,/var目录中存放的是可变的数据。比如spool目录(mail,news),log文件,临时文件
  10. /proc目录,这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,他没有实际的存储设备,里面的目录,文件都是由内核临时生成的,用来标识系统的运行状态,也可以操作其中的文件控制系统
  11. /mnt目录,用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一个子目录,比如/mnt/cdram,/mnt/hda1。用来临时挂载光盘、硬盘
  12. /tmp目录,该目录用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以tmp目录必须存在并可以访问。
[16]挂载U盘
  1. 挂载

    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盘了
    
  2. 卸载(解挂载)

    ##首先应该退出挂载的目录,上述指的是usb目录,然后再执行解挂载命令
    sudo umount /dev/sdb1   ##或者下述代码
    sudo umount /mnt/usb   ##卸载U盘文件系统
    
  3. 若是无法识别U盘,可能是协议不一样,USB2.0不兼容USB3.0,所以可以在 虚拟机--》 设置 --》 USB控制器 里面可以更改,3.0向下兼容2.0

[17]网络管理(windows和linux系统下)
  1. 见老师笔记里面的word文档
  2. ipconfig ping ifconfig 三个命令

day3

[1]ubuntu中系统环境变量的设置
  1. env命令,打印ubuntu系统的所有的环境变量

  2. PATH,系统环境变量

  3. 如何把自己可执行的文件当成一个系统命令运行

    • 拷贝自己的可执行文件程序到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]第一个脚本文件
  1. 脚本文件后缀为sh

  2. 脚本文件的注释

    • 单行注释: #

    • 多行注释:

      :<<!
      多行注释处
      !
      
  3. 一条shell命令占一行,不用分号结尾,一条shell命令如果无法写完,需要加""续行符

  4. 脚本文件第一行内容为 #!/bin/bash 意思是使用bash解析器

  5. 执行shell脚本3种方式

    • ./test.sh:要求脚本文件必须具有可执行权限,重新打开一个子终端下执行shell程序,将执行结果放回当前终端
    • source ./test.sh:脚本文件不需要具有可执行权限,在当前终端下执行hsell脚本
    • bash ./test.sh :脚本文件不需要具有可执行权限,重新打开一个子终端下执行shell程序,将执行结果放回当前终端
[7]shell变量的定义
  1. variablename=变量值
    variablename='变量值'
    variablename="变量值"
    
    • 等号两边不允许出现空格
    • 如果变量值中没有空格,上面3中方式都一样,但是如果有空格,就需要单引号和双引号来设置边界
    • 双引号会组织shell对大多数特殊字符进行解释。但"$"、"`"、"”"仍然保持其特殊含义。而单引号会组织shell对所有字符进行解释,不会对特殊字符进行解释,所以变量中要引用其他变量时,不能使用单引号
    • shell变量名字的规则:和C语言一致
  2. 使用变量,注意用花括号和不用花括号的场合

  3. unset 用来删除变量,但是不能删除只读变量,unset 变量名

  4. 只读变量的设置,使用readonly关键字进行修饰

    readonly str1="caiji"
    
  5. 命令置换:倒引号``或者 $ ()

    倒引号:当倒引号括起一个shell命令时,这个命令将会被执行,执行后的输出结果将作为这个表达式的值。倒引号中的特殊字符一般都被解释

  6. 练习:全程用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件同时传递参数
  1. 类似于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"
    
  2. 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中特殊的变量
  1. $0 :获取可执行文件的名字

  2. $n:(n > 1)获取脚本文件执行传递的参数,echo $1 $2 $3

  3. $#:获取脚本文件执行传递的参数的个数,但是和C语言里面不一样,他不算argv[0],即,不计算$0

  4. $*:获取脚本文件执行时传递的所有参数,相当于把$1、$2、$3……全部一起输出

  5. $@:和$*一样

  6. $?:获取上一条命令的退出状态,上一条命令成功退出状态就是0,否则为一个大于0的数

  7. $$:获取当前shell进程的ID号

    注:$*和$@的区别

    • 在大多数情况下没有区别,但是加上双引号之后有区别

    • $*加上双引号之后,会看成一个整体的字符串处理

    • $@加上双引号后,会当成多个字符串处理,以空格为分隔符

      #! /bin/bash
      
      function func()
      {
      	echo $1
      	echo $2
      	echo $3
      }
      
      echo $$
      echo $#
      func $*   
      func $@
      func "$*"  ##打印$1,他会把你输入的所有的字符串都看成一个字符给$1,另外两个没有就显示空行
      func "$@"  #
      
[10]ps命令
  1. ps aux 命令能够显示当前系统上运行的所有进程的信息(可以结合 grep使用),查看状态
  2. ps -ef 主要用于查看进程号
  3. ps lax 可以提供父进程 ID(PID) 和谦让度( NI ),ps lax 命令不会显示进程属主的用户名,因此运行速度更快

day4

[1]、shell中的数组
  1. shell中只支持一维数组,不支持多维数组

  2. 数组的定义

    (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
  1. 由于shell中所有变量都是用字符串处理,如果直接使用运算符会当成字符串来处理,此时要达到运算的目的,必须使用shell中提供的特殊运算符
[3] (())运算
  1. (())用于整数计算,不可以用小数或或者字符串,(())里面会解析表达式中引用的变量,所以变量可以省略$,当然也可以不用省略

    ((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运算命令
  1. 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对于字符串的运算
  1. 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 运算命令
  1. 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
      
  2. 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……
  1. if语句,以 fi 作为结束标志,继续执行后面的脚本

    if 条件
    then
    	命令
    else
    	命令2
    fi  
    ##或者
    if 条件;then
    	命令
    else
    	命令2
    fi  
    
  2. if……elif……

    if 条件1
    then
    	命令1
    elif 条件2
    then
    	命令2
    ……
    else 
    	命令
    fi
    
  3. 练习:根据输入的成绩,判断优、良、及格、差

    ##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"
    fi
    

    return 和 exit

    #! /bin/bash
    
    num1=9
    
    if ((num1 == 9))
    then
    	echo "Yes"   
    	#return   使用return 的时候只能用source命令执行,因为return返回一个值,但               他是在子终端中显示,执行完关闭了子终端,这时候会报错
    	exit  ##这种就适用bash和./来执行,在子终端中exit退出终端
    fi
    echo "----------------"
    
[8]test命令
  1. test是一个shell的一个内置命令,主要用于判断条件是否成立,test命令常常和if和while语句结合使用

    • type 命令 可以判断命令是否为内置命令还是外置命令
    • 一般返回结果包括buildin就为内置命令
  2. test命令可以简写为[ ],这个时候的中括号里面可能要用转义 括号、大于小于。test 1 + 2 等价于 [ 1 + 2 ] ,因为test是命令,所以空格不可省略

  3. 文件测试

    选项 描述
    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文件存在并且可以执行,就执行,否则就什么也不做
    
  4. 数值比较相关的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
    
  5. 复合表达式

    操作符 描述
    ! 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" ]
  6. 与字符串判断相关的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
  7. 与逻辑运算符相关的test选项

    注意:如果[]命令中引用其他的变量,建议引用变量是加上""

[7] case...in 语句

一、语法

case expression in
	pattern1)
		命令1
		;;
	pattern2)
		命令2
		;;  ##相当于break
	……
	*)   ##相当于default
		命令
		;;
esac

二、注意事项

  1. 最后一个分支的两个分号可以省略,但是其他分支中的分号都不能省略

  2. exprssion 可以是变量、表达式、数字、字符串

  3. pattern 可以是数字、字符、字符串、正则表达式

  4. 正则表达式(真值表达式)

    *   ---> 匹配所有的字符 
    [abc]  --> abc中的任意一个 
    [a-c]  --> a-c之间的任意一个 
    [a-zA-Z] -->所有的英文字符包括大小写 
    [1-9]  --> 数字1-9之间的任意一个 
    "abc" | "ABC"  --> 字符串adc或ABC任意一个
    

三、练习题

  1. 成绩分类使用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循环

  1. 格式

    for index in list   ##index为变量时不需要用$符号
    do
    	command
    done
    
  2. 执行过程:从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 $n
      • for 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
  1. break:语法格式,break num,其中num的值要大于1,等于1的时候可以省略,它的作用是跳出 num 层循环
  2. continue:语法格式,continue num ,其中num的值要大于1,等于1的时候可以省略,它的作用是跳出 当前的num 层循环,执行下一次的循环
[4] shell 中的函数

一、语法格式

function functionName()
{
	函数体
	返回值
}

二、注意

  1. 函数没有形参,调用函数时,传递实参就有参数,不传递参数就无参数

  2. 函数不需要指定返回类型

  3. 函数需要 function 关键字修饰

  4. 函数内部获取传递的参数的方式

    • $1 $2 $3……
    • $* 所有参数
    • $@ 所有参数
  5. 函数调用方式

    • 函数没有参数,没有返回值

      funname
      
    • 函数有形参,没有返回值

      functionName 参数1 参数2...
      
    • 函数有返回值

      sum=$(functionNama 参数1 参数2 参数3...)
      
  6. shell中所有的变量都是全局变量,即时在函数体内部定义的变量依然是一个全局变量

  7. 如果想在函数中的变量定义为局部变量需要使用 local 关键字修饰

  8. $? 获取上一条指令的执行的状态,上一条命令执行成功返回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

  1. makefile:是用于项目工程管理的文本文件

  2. make:make 是一个可执行程序,是ubuntu系统中的一个命令,make 可执行程序在 /usr/bin目录下,当我们在终端中执行make命令时,make 命令会在当前目录下找到 Makefile 文件,对 Makefile 文件进行解析,并且执行 Makefile 中的语句,完成对工程的编译

  3. Makefile 用途:Makefile 中主要用于工程文件编译,Makefile中写的是编译工程的一些命令,当你执行 make 时,会执行 Makefile 中的命令,完成对整个工程的编译,最终生成一个可执行文件

  4. 要掌握程序的编译流程

    ##预处理  编译  汇编  链接
    gcc -E *.c -o *.i
    gcc -S *.i -o *.s
    gcc -c *.s -o *.o
    gcc *.o -o *.elf
    
  5. 要具有一定面向依赖的思想

    ##  *.i 依赖 *.c
    ##  *.s 依赖 *.i
    ##  *.o 依赖 *.s
    ##  *.elf 依赖 *.o
    
[1]Makefile 中的语法规则
  1. 语法规则的构成

    目标:目标的依赖
    	(tab)shell命令
    
  2. 注意

    • 目标必须顶格写
    • shell命令前边必须是一个tab键,不能用4个空格代替
    • Makefile 文件中可以包含多个规则,至少要有一个规则
    • 一个规则中可以没有目标的依赖
    • 一个规则中可以没有shell命令
    • 命令前面加@,可以取消命令的回显
  3. 执行make命令

    • 如果后面没有接目标,那Makefile会默认找到Makefile 文件的第一个目标,执行第一个目标后边的命令
    • 如果 make 后面加上相应的目标,那么会执行对应目标后边的命令
    • make -jn 目标 开辟n个线程执行Makefile文件,对工程进行编译,提高编译的效率
  4. 案例

    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   ##可以没有命令
    
  5. 实际项目,假如我有一个相加函数,相减函数,和一个自己定义的头文件,然后就是一个主函数

    //主函数
    #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
    
  6. Makefile 执行过程

    • 首先进入编译的工程目录,执行make 命令
    • 他会自动找到工程目录下的Makefile 文件,进行依赖关系解析,生成依赖关系树
    • 命令执行阶段。他会把解析的依赖关系树加载到内存,安装依赖关系,按照顺序执行命令,声称对应的文件
  7. 注意

    • 如果已经执行过依次 make 编译且没有删除相关文件的话,此时再次执行make 进行编译时,make会检查文件的时间戳,判断生成的文件时间是否早于对应源文件
    • 如果比源文件生成的早,就证明源文件进行过修改,执行 make 时不会重新编译源文件,使用之前生成的生成文件,这样的目的主要是为了缩短编译的时间
[2] Makefile 中的变量
  1. 变量的定义

    • variable = value,使用 variable 变量时,才将value 赋值给variable ,延时赋值(赋值)
    • variable := value,立即赋值
    • variable ?= value,判断 variable 是否有初始值,如果有初始值,就不对变量进行赋值,如果没有初始值,就对变量进行赋值
    • variable += value,追加赋值,variable 中原有的值和 value 的值之间用空格隔开
  2. 变量的使用

    • $(variable) 注意和shell中变量使用的区别
  3. 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)"
      
  4. 简写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
    
  5. 函数

    • 模式替换函数-- 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
      
  6. 把 .c .o 文件分别放在自己的目录中

    • 加前缀函数 -- addprefix
    • $(addprefix prefix, names1...),该函数会将 name 中的每一个文件添加前缀 prefix ,参数 name1 是以空格分割的文件名序列
    • 返回值:以单空格分隔的添加了前缀 prefix 的文件名序列
    • 示例:$(addprefix src/, foo bar) ,返回值为 src/foo src/bar
posted @ 2021-06-22 07:53  今天的风,甚是喧嚣  阅读(278)  评论(0)    收藏  举报