文章目录
第三章:Vim编辑器与Shell命令脚本
为了进一步学习Linux系统,我们需要能够自己编写脚本
因此我们首先需要学习编写脚本工具–Vim编辑器.
然后使用Vim编辑器来编写,修改文档.通过逐个配置主机名称,系统网卡以及Yum转件参数参数文件等来加深Vim编辑器中诸多命令,快捷键,模式切换方法的理解
接下来将前面学到的Linux命令,命令语法和Shell脚本中的流程控制语句通过Vim编辑器写到Shell脚本中,创建出自己的命令/脚本
最后本章还额外讲解了at命令和crond命令,来让日常的工作更加高效
Vim文本编辑器
Linux中,万物皆文件.
例如:我们对于配置计算机中的某个硬件设备时候,将这个设备的参数写成文件,放在/dev文件下,进而达到配置硬件的目的
除此以外,我们还需要编写脚本,修改文件等等.因此就需要我们的Vim文本编辑器来实现.
Vim文本编辑器是默认安装在所有Linux系统上的,是Vi文本编辑器的升级.
Vim文本编辑器非常强大,因为它相比于一般的文本编辑器,实现了通过键盘完成一切其他文本编辑器的所有功能,因而极大地提高了效率
具体的实现方法,就是各种各样的快捷键,因此学习曲线比较陡峭,但是花上两个周来学习却能为以后剩下数月的时间.
Vim文本编辑器具有三种模式:命令模式,输入模式,末行模式
- 命令模式:控制光标移动,对文本进行复制,粘贴,删除和查找工作
- 输入模式:正常的文本输入
- 末行模式:保存或退出文档以及设置编辑环境.因为保存或者退出文档往往是最后一步,所以称为末行模式
因此,一个一般的Vim文本编辑器编写流程如下:
- 打开Vim文本编辑器,默认进入命令模式,在此模式下控制光标移动到指定位置或者将文本进行复制,粘贴,删除和查找
- 将光标移动到指定位置之后或者已经完成复制,粘贴,删除或者查找后切换到输入模式进行输入.但是在输入模式下无法进行保存或退出操作
- 完成编辑后,进入末行模式,完成保存或退出
因此,Vim编辑器中内置了成百上千的命令,我们这里只讲解最基本的命令,满足存活下来的必要
Vim中的命令
打开Vim编辑器默认进入命令模式,如果想要进入输入模式,按下[a],[i],[o]等键;如果想要进入末行模式,输入:([Shift]+[;])
进入输入模式后,想要返回命令模式按下[Esc];在末行模式,想要返回命令模式按下[Esc]
输入模式不能直接进入末行模式,需要先返回命令模式.
命令模式中的常用命令
命令 | 作用 |
---|---|
dd | 删除(剪切)光标在的整行 |
5dd | 删除(剪切)从光标处开始的5行 |
yy | 复制光标所在的整行 |
5yy | 复制从光标处开始的5行 |
n | 显示搜索命令定位到的下一字符串 |
N | 显示搜索命令定位到的上一字符串 |
u | 撤销上一步的操作 |
p | 将之前删除(dd)或者赋值(yy)过的数据黏贴到光标后面 |
末行模式用于保存或退出文件,以及设置Vim文本编辑器的工作环境,还可以让用户执行外部的Linux命令或跳转到所编写文档的特定行数.
末行模式的常用命令
命令 | 作用 |
---|---|
: w | 保存 |
: q | 退出 |
: q! | 强制退出(放弃对文档的修改内容) |
: wq! | 强制保存退出 |
: set nu | 显示行号 |
: set noun | 不显示行号 |
: 命令 | 执行该命令 |
: 整数 | 跳转到改行 |
: s/one/two | 将当前光标所在行的第一个one替换成two |
: s/one/two/g | 将当前光标所在行的one替换成two |
: %s/one/two/g | 将全文中的所有one替换成two |
?字符串 | 在本文中从下至上搜索该字符串 |
/字符串 | 在文本中从上至下搜索该字符串 |
这里只是对Vim编辑器有最基本的讲解,下面是配合的最基本的Vim练习
事实上,如果真的想要熟练掌握使用Vim编辑器,推荐参考CSDN博主的翻译文章:Vim从入门到放弃
传送门
Vim文本编辑器练习
Vim配置主机名
首先我们将以配置主机为例,练习Vim文本编辑器
主机名大多保存在/etc/hostname文件夹下.下面将修改其中的文件名
步骤如下:
-
使用vim命令,进行修改
vim /etc/hostname
-
vim编辑器下进行修改,并使用:wq!命令退出
-
然后使用hostname来查看主机名是否修改成功
hostname
Vim配置网卡
正如前面所说,Linux中万物皆文件.
因此我们配置网卡的本质还是修改文件.
存放网卡配置文件路径是/etc/sysconfig/network-script
由于每台设备的硬件和架构是不一样的,因此需要用ifconfig命令自行确认默认网卡的默认名称
所以步骤就是
- 进入network-script目录
- 修改其中的ifconfig-eno1677736文件
- 重启网络服务,测试网络是否联通
具体代码见书
配置Yum软件仓库
正常我们安装一个软件(以源代码方式),那么首先需要编译源码,连接文件,生成可执行文件,最后才安装成功
为了避免这种麻烦的操作,Yum软件仓库的作用就是简化这种安装方式(RPM管理软件)的难度和自动分析所需要的软件包和依赖关系的技术
下面就将配置Yum软件仓库
Yum软件仓库配置文件在/etc/yum.repos.d/目录中,所以我们首先需要进入该目录,配置其中的文件(一开始是没有配置文件的,所以需要我们自己创建)
然后将配置参数的路径挂载到光盘,并且把光盘挂载信息写入到/etc/fstab文件中
接下来安装即可
不过本人按照书上的步骤进行,会报错,在挂载过程中报错找不到介质.在csdn上搜索即可
编写Shell脚本
Shell脚本实际上是将多句命令放在一起形成的文本.其本质上是为了让多句语句能够协同工作.但是由于有的语句具有返回值,因此不同的语句之间可能会有分支循环等关系.
Shell终端解释器是人与计算机硬件之间的"翻译官",他作为用户和Linux系统内部的通信媒介,用于翻译,执行Shell终端解释器
Shell终端解释器不仅支持各种变量与参数外,提供了诸如循环,分支等高级编程语言采用的控制野心
Shell脚本命令的工作方式有两种: 交互式和批处理
其中,交互式是指用户每输入一条命令就立即执行,批处理是用户事先编写好一个完整的Shell脚本,Shell会一次性执行脚本中的诸多命令
Shell脚本中不仅会用到前面学习过的很多Linux命令及正则表达式,管道符和数据流重定向等语法规则.还需要把内部功能模块化之后通过逻辑语句进行处理,最终形成日常所见的Shell脚本
Shell脚本的编写及运行
Shel脚本的编写很简单,只需要用Vim编辑器吧Linux命令按顺序依次写入到一个文件中,就是一个简单的脚本了
例如编写脚本自动输出当前工作目录及当前工作目录下的文件和属性信息
-
首先创建一个文件,最好以.sh结尾,作为Shell脚本文件标志
vim example.sh
-
编辑如下内容
#!/bin/bash #脚本声明,用#!告诉系统用哪种Shell解释器来执行脚本 #For example by JackWang #注释信息 clear pwd ls -al
-
运行该脚本,可以直接用bash解释器来运行
bash example.sh
或者给予执行权限
chmod u+x example.sh
然后直接执行即可
./example.sh
接受用户参数
就像我们使用其他命令一样,我们在后面加上选项或者文件地址等等由用户决定的参数,我们自己编写的脚本实际上也可以接受用户的参数
这样就具有了极大的灵活性.
我们在一个语句后面所接的就是参数,例如
./example.sh one two three four five six
分别对应着6个变量.我们可以用$
来提取这些位置上的变量
事实上我们可以传递n个参数
例如
vim example.sh
#!/bin/bash
#For example by JackWang
echo "当前脚本名为$0"
echo "总共用$#个参数,分别是$*"
echo "第一个参数是$1"
其中0号变量是脚本名,*通配所有变量,#用户输入参数数量
运行脚本
./example.sh one two three four five six
得到结果
当前脚本名为./example.sh
总共用6个参数,分别是one two three four five six
第一个参数是one
测试语句
我们在掌握Linux基础命令之后,掌握Shell脚本语法变量和接受用户输入的信息之后,好需要进一步处理接收到的用户参数
为此,我们需要掌握测试语句
测试语句的语法如下
[ 条件测试语句 ]
需要注意的是两个括号和条件测试语句之间都有空格
当条件测试语句中的条件表达式成立,整个式子将返回0,否则将返回任意一个非零数
根据测试对象的不同,条件测试语句具体又可以分为:
- 文件测试语句
- 逻辑测试语句
- 整数值比较语句
- 字符串比较语句
下面将一一讲解
文件测试语句
文件测试语句是使用指定条件判断对应文件是否存在或者权限是否满足等情况的运算符.
具体参数如下:
运算符 / 参数 | 作用 |
---|---|
-d | 测试文件是否为目录类型 |
-e | 测试文件是否存在 |
-f | 判断是否为一般文件 |
-r | 测试当前用户是否有权限读取 |
-w | 测试当前用户是否有权限写入 |
-x | 判断当前用户是否有权限执行 |
下面使用文件测试语句来判断/etc/fstab
是否为一个目录文件,然后通过$
提取内设的?
变量,?
变量存储的上一个语句执行的结果,执行成功返回0,否则返回任意数字
[root@JackWang Desktop]# [ -d /etc/fstab ]
[root@JackWang Desktop]# echo $?
1
返回值为1,表示这个文件并不是一个目录,我们再使用file命令验证下
[root@JackWang Desktop]# file /etc/fstab
/etc/fstab: ASCII text
可以发现/etc/fstab是一个ASCII码写成的文本文件
我们在使用-f
参数来测试
[root@JackWang Desktop]# [ -f /etc/fstab ]
[root@JackWang Desktop]# echo $?
0
可以发现返回值为0,符合我们之前查询的结果
逻辑测试语句
逻辑测试语句用于对测试的结果进行逻辑分析,根据不同的测试结果实现不同的效果
与逻辑&&
与逻辑&&只有当前面命令的返回值为真 / 0 时才会执行后面的命令
我们继续使用上面的例子
[ -e /etc/fstab ] && echo "/etc/fstab exists"
运行结果为
[root@JackWang Desktop]# [ -e /etc/fstab ] && echo "/etc/fstab exists"
/etc/fstab exists
或逻辑||
或逻辑是只有当前面的命令执行失败后,才会执行
继续使用上面的例子,我们已经知道/etc/fstab是一个文本文件
[root@JackWang Desktop]# [ -d /etc/fstab ] ||echo $"/etc/fstab is not a directory"
/etc/fstab is not a directory
我们也可以判断当前登录用户是否为非管理员用户
[root@JackWang Desktop]# [ $USER = root ] || echo $"This is not root"
[root@JackWang Desktop]# su - jwang
Last login: Tue Jun 30 20:57:39 EDT 2020 on pts/0
[jwang@JackWang ~]$ [ $USER = rot ] || echo $"This is not root"
This is not root
[jwang@JackWang ~]$
非逻辑!
非逻辑就是把条件测试语句中的判断结果取反
同样使用上面的例子
[root@JackWang ~]# [ ! -d /etc/fstab ] && echo $"This is ot a directory"
This is ot a directory
最后我们综合三个逻辑,得到如下判断登录用户的语句
[ $USER = root ] && echo $"root" || echo $"user"
[root@JackWang ~]# su - jwang
Last login: Wed Jul 1 02:47:35 EDT 2020 on pts/0
[jwang@JackWang ~]$ [ $USER = root ] && echo $"root" || echo $"user"
user
整数比较运算符
整数比较运算符的运算对象仅是整数,不能将数字和字符串,文件等放在一起比较
由于等号,大于号和小于号等生活中的数字比较符号和重定向符冲突了,所以Linux中的整数比较运算符重新规定了(其实是参数)
运算符 | 作用 |
---|---|
-eq | 是否等于 |
-ne | 是否不等于 |
-gt | 是否大于 |
-lt | 是否小于 |
-le | 是否小于或等于 |
-ge | 是否大于或等于 |
我们可以判断10是否大于10
[jwang@JackWang ~]$ [ 10 -eq 10 ]
[jwang@JackWang ~]$ echo $?
0
我们接下来用整数比较来判断当前内存可用量是否小于1024
对此我们首先需要知道当前内存使用量是多少,我们可以使用free命令查询
free
total used free shared buffers cached
Mem: 1870760 1695876 174884 10960 1592 615772
-/+ buffers/cache: 1078512 792248
Swap: 2097148 0 2097148
其中Mem的第四列(第三列数字)是空闲的内存,是我们要找的
所以我们可以先用free查询,然后用管道符传递给grep后查询Mem:,得到这一行数据
[jwang@JackWang ~]$ free -m | grep Mem:
Mem: 1826 1657 168 10 1 601
接下来用awk "{print $4}"来打印第四列的数据
[jwang@JackWang ~]$ free -m | grep Mem: | awk '{print $4}'
167
然后我们在判断内存是否充足
[jwang@JackWang ~]$ FreeMem=`free -m |grep Mem: |awk '{print $4}'`[jwang@JackWang ~]$ [ $FreeMem -ge 1024 ] && echo $"内存充足" || echo $"内存不足1024Mb"
内存不足1024Mb
字符比较运算符
字符比较运算符用于比较字符串是否为空,两个字符串是否相同
字符比较运算符一般用于判断某个变量是否被定义(即内容是否为空值),
常见的字符比较运算符有
运算符 | 作用 |
---|---|
= | 比较字符串内容是否相同 |
!= | 比较字符串内容是否为空 |
-z | 判断字符串内容是否为空,返回0表示为空,1为非空 |
一个没有定义的变量字符串是空值,所以我们能够借助字符比较运算符来判断某个变量是否定义
下面我们首先判断String这个变量是否已经被定义过
[jwang@JackWang ~]$ [ -z $String ]
[jwang@JackWang ~]$ echo $?
0
表明String这个变量没有被定义过
我们还可以结合逻辑判断符来判断系统当前语系是否为英语
[jwang@JackWang ~]$ [ $LANG = "en.us" ] && echo $"This is en.us" || echo $" This is not en.us"
This is not en.us
[jwang@JackWang ~]$ echo $LANG
en_US.UTF-8
流程控制语句
尽管此时我们已经能够让我们编写的Shell脚本运行普通的命令,接受用户的参数,判断用户的参数,但是这些是不够的.
例如我们如果编写自己的touch命令的话,创建文件时就分为文件存在和文件不存在两种情况,这样的话单纯的命令是不足以完成任务的.
对应的,我们需要判断语句.
因此为了能够让我们编写的Shell更好的工作,我们需要引入流程控制语句,包括: 分支,循环,以及顺序
顺序结构就是Shell脚本从上到下依次执行,因此这里主要讲解分支和循环结构
if 条件测试语句
if条件测试语句用于形成分支结构,语法如下
if 条件
then 命令序列1
elif 命令序列3
else 命令序列3
fi
下面使用if来在/media/cdrom文件不存在的情况下创建该文件
vim mkcdrom.sh
#! /bin/bash
DIR="/media/cdrom"
if [ -e $DIR ]
then
mkdir -p $DIR
fi
下面使用双分支结构验证某台主机是否在线,然后根据返回值的结果,显示信息.
这里主要用ping命令来测试与对方的联通性,进而判断对方是会否在线
此外,Linux中的ping命令不会自动停止,因此我们需要用-c参数来制定测试次数,-i指定发送键哥,-W定义等待超时时间
vim chkhost.sh
#! /bin/bash
ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
if [ $? -eq 0 ]
then
echo "Host $1 is online"
else
echo "Host $1 is not online"
fi
此外/dev/null文件被称为Linux黑洞文件,把输出的信息重定向到这里等同于删除这个文件,并且无法恢复
下面将编写脚本,让用户输入成绩,脚本判断成绩是Excellent还是Pass还是Fail
Linux中用read命令来读入用户的输入信息,能把接受到的信息赋值给后面的变量,-p参数用于指定输出提示信息
#! /bin/bash
read -p "Enter your grade(0~100) :" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]
then
echo $"$GRADE is Excellent"
elif [ $GRADE -ge 60 ] && [ $GRADE -le 85 ]
then
echo $"$GRADE is Pass"
elif [ $GRADE -ge 0 ] && [ $GRADE -le 60 ]
then
echo $"$GRADE is Fail"
else
echo $"Are you sure?"
fi
case条件测试语句
case条件测试语句其实就是Linux中的switch语句
语法如下
case 变量 in
模式1)
命令序列1
;;
模式2)
命令序列2
;;
模式3)
命令序列3
;;
...
模式n)
命令序列n
;;
*)
默认命令序列
esac
下面我们编写一个脚本,来判断用户输入的到底是数字还是字符还是其他字符
#!/bin/bash
read -p "请输入一个按键:" KEY
case "$KEY" in
[a-z]|[A-Z])
echo "您输入的是字母"
;;
[0-9])
echo "您输入的是数字"
;;
*)
echo "您输入的既不是数字也不是字母"
esac
for循环语句
for循环语句用于已知次数的循环
语法如下
for 变量名 in 变量取值列表
do
命令序列
done
While循环语句
While条件循环语句用于未知次数的循环
语法如下
while 条件测试语句
do
命令序列
done
下面我们编写一个猜测价格的脚本
首先我们可以用$RANDOM这个系统内部变量来生成一个随机数,然后用expr命令来取得数值.接下来获取用户的输入,和前面相同,使用read 命令,-p参数来输出提示信息,如果猜对的话,使用exit 0来退出脚本运行
#! /bin/bash
PRICE=$(expr $RANDOM % 1000)
TIME =0
echo $"商品的价格在0~999之间,猜猜看是多少:"
while true
do
read -p "请输入你猜测的价格:" INPUT
let TIME++
if [ $INPUT -eq $PRICE ]
then
echo $"恭喜你猜对了,价格是$PRICE"
exit 0
elif [ $INPUT -gt $PRICE ]
then
echo $"太高了"
else
echo $"太低了"
fi
done
以上就是简单的Shell脚本语句.其实说白了,Shell脚本只是多个语句的集合,其中的每一个保留字都是一个命令,我们运行Shell脚本就是让bash解释器去执行其中的每一条语句
计划任务服务程序
最后,我们已经能够编写Shell脚本来自动化完成任务了,我们还需要最后一点就是掌握自动使用我们编写的Shell脚本的语句
为此,我们需要学习计划任务服务语句
计划任务服务语句可以分为一次性任务和长期性任务.
一次性任务只执行一次,对应的命令是
at 时间
例如今晚11:30分开启网站服务
[root@JackWang Desktop]# at 23:30 #注意,当我们回车之后,就会进入at模式,类似于vim和输入重定向中的分界符
at> systemctl restart httpd
at> <EOT> #要在此处按下Ctrl+D来退出
job 1 at Thu Jul 2 23:30:00 2020
如果我们想要查看已经设置好但是没有执行的任务,就使用-l
参数
[root@JackWang Desktop]# at -l
1 Thu Jul 2 23:30:00 2020 a root
当然我们也可以使用echo命令来提取,然后通过管道符传递给at
[root@JackWang Desktop]# echo "systemctrl restart httpd" | at 23:30
job 2 at Thu Jul 2 23:30:00 2020
[root@JackWang Desktop]# at -l
1 Thu Jul 2 23:30:00 2020 a root
2 Thu Jul 2 23:30:00 2020 a root
我们如果想要删除已经设置好的任务
那么直接用atrm命令即可
语法如下
atrm 任务号
例如删除第二个任务
[root@JackWang Desktop]# at -l
1 Thu Jul 2 23:30:00 2020 a root
2 Thu Jul 2 23:30:00 2020 a root
[root@JackWang Desktop]# atrm 2
[root@JackWang Desktop]# at -l
1 Thu Jul 2 23:30:00 2020 a root
如果我们需要Linux系统进行周期性的,有规律的任务,那么我们就需要用到crond服务
其中
创建,编辑计划任务的命令是
crontab -e 分 时 日 月 星期 命令
其中,分是0-59之间的任意整数,时是0-23之间的任意整数,日是1-31之间的任意整数,月是1-12之间的任意整数,星期时候0-7之间的任意整数,其中0和7都代表星期日
如果不设置该时段,则要用*占位
可以用逗号连接多个时间,例如在星期一和星期三都执行任务,那么在星期对应的参数栏使用1,3
可以使==-连接两个时间点表示时段内周期执行
可以用*/数字==表示循环频率,这里表示每两秒执行一次
此外,命令要写成绝对路径的方式
查看当前任务计划的命令是
crontab -l
删除某个计划任务的命令是
crontab -r
此外,如果以管理员身份登录系统,想要修改他认得任务还要加上==-u==参数