第四章 Vim编辑器与Shell命令脚本
1 Vim 文本编辑器
三种模式
- 命令模式:控制光标移动,可对文本进行复制、粘贴、删除和查找等工作。
- 输入模式:正常的文本录入。
- 末行模式:保存或退出文档,以及设置编辑环境。
命令模式
末行模式
输入模式
- 可以分别使用 a、i、o 这 3 个键从命令模式切换到输入模式。其中,a 键与 i 键分别是在光标后面一位和光标当前位置切换到输入模式,而 o 键则是在光标的下面再创建一个空行
2 编写Shell脚本
Shell脚本命令的两种工作方式
- 交互式:用户每输入一条命令就立即执行。
- 批处理:由用户事先编写好一个完整的 Shell 脚本,Shell 会一次性执行脚本 中诸多的命令。
简单的Shell脚本
[root@linuxprobe~]# vim example.sh
#!/bin/bash
#For Example BY linuxprobe.com
pwd
ls -al
第一行的脚本声明(#!)用来告诉系统使用哪种 Shell 解释器来执行该脚本;第二行的注释信息(#)是对脚本功能和
某些命令的介绍信息,使得自己或他人在日后看到这个脚本内容时,可以快速知道该脚本的
作用或一些警告信息;第三、四行的可执行语句也就是我们平时执行的 Linux 命令了。
-
脚本运行方式
-
用Bash 解释器命令直接运行 Shell 脚本文件
bash example.sh
-
通过输入完整路径的方式来执行(默认会因为权限不足而提示报错信息)
[root@linuxprobe~]# ./example.sh bash: ./Example.sh: Permission denied [root@linuxprobe~]# chmod u+x example.sh [root@linuxprobe~]# ./example.sh
-
接受用户的参数
-
变量参数
-
$0 对应的是当前 Shell 脚本程序的名称
-
KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲对应的是总 共有几个参数,*对应的是所有位置的参数值
-
$?对应的是显示上一次命令的执行返回值
-
$1、$2、$3……则分别对应着第 N 个位置的参数值
-
[root@linuxprobe~]# vim example.sh
#!/bin/bash
echo "当前脚本名称为$0"
echo "总共有$#个参数,分别是$*。"
echo "第 1 个参数为$1,第 5 个为$5。"
[root@linuxprobe~]# bash example.sh one two three four five six
当前脚本名称为 example.sh
总共有 6 个参数,分别是 one two three four five six。
第 1 个参数为 one,第 5 个为 five。
判断用户的参数
-
条件表达式
- Shell 脚本中的条 件测试语法可以判断表达式是否成立,若条件成立则返回数字 0,否则便返回非零值。
- 条件表达式两边均应有一个空格。
- 4种条件测试语句:文件测试语句、逻辑测试语句、整数值比较语句、字符串比较语句
-
文件测试语句
-
判断/etc/fstab 是否为一个目录类型的文件,然后通过 Shell 解释 器的内设$?变量显示上一条命令执行后的返回值。
[root@linuxprobe~]# [ -d /etc/fstab ] [root@linuxprobe~]# echo $? 1 #来判断/etc/fstab 是否为一般文件,如果返回值为 0,则代表文件存在,且为一般文件 [root@linuxprobe~]# [ -f /etc/fstab ] [root@linuxprobe~]# echo $? 0 #一次搞定 判断/dev/cdrom 文件是否存在,若存在则输出 Exist 字样 [root@linuxprobe~]# [ -e /dev/cdrom ] && echo "Exist" Exist
-
-
逻辑测试语句
与(&&)、或(||)、非(!)
-
结合系统环境变量 USER 来判断当前 登录的用户是否为非管理员身份
[root@linuxprobe~]# echo $USER root [root@linuxprobe~]# [ $USER = root ] || echo "user" [root@linuxprobe~]# su - linuxprobe [linuxprobe@linuxprobe~]$ [ $USER = root ] || echo "user" user
-
判断当前用户是否为一个非管理员的用户
[linuxprobe@linuxprobe~]$ exit logout [root@linuxprobe~]# [ ! $USER = root ] || echo "administrator" administrator #叹号应该放到判断语句的前面,代表对整个的测试语句进行取反值操作,而不应该写成“$USER != root”,因为“!=”代表的是不等于符号(≠),尽管执行效果一样,但缺少了逻辑关系
-
-
整数值比较语句
-
[root@linuxprobe~]# free -m total used free shared buff/cache available Mem: 1966 1374 128 16 463 397 Swap: 2047 66 1981 [root@linuxprobe~]# free -m | grep Mem: Mem: 1966 1374 128 16 463 397 [root@linuxprobe~]# free -m | grep Mem: | awk '{print $4}' 128 [root@linuxprobe~]# FreeMem=`free -m | grep Mem: | awk '{print $4}'` [root@linuxprobe~]# echo $FreeMem 128
-
使用整数运算符来判断内存可用量的值是否小于 1024,若小 于则会提示“Insufficient Memory”(内存不足)
[root@linuxprobe~]# [ $FreeMem -lt 1024 ] && echo "Insufficient Memory" Insufficient Memory
-
-
字符串测试语句
-
判断 String 变量是否为空值,进而判断是否定义了这个变量
[root@linuxprobe~]# [ -z $String ] [root@linuxprobe~]# echo $? 0
-
当用于保存当前语系的环境变量值 LANG 不是英语 (en.US)时,则会满足逻辑测试条件并输出“Not en.US”(非英语)
[root@linuxprobe~]# echo $LANG en_US.UTF-8 [root@linuxprobe~]# [ ! $LANG = "en.US" ] && echo "Not en.US" Not en.US
-
3 流程控制语句
if测试语句
[root@linuxprobe~]# vim chkscore.sh
#!/bin/bash
read -p "Enter your score(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ] ; then
echo "$GRADE is Excellent"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] ; then
echo "$GRADE is Pass"
else
echo "$GRADE is Fail"
fi
[root@linuxprobe~]# bash chkscore.sh
Enter your score(0-100):88
88 is Excellent
[root@linuxprobe~]# bash chkscore.sh
Enter your score(0-100):80
80 is Pass
for条件循环语句
- 批量创建用户
#创建用户名称的列表文件 users.txt
[root@linuxprobe~]# vim users.txt
andy
barry
carl
duke
eric
george
#批量创建脚本
[root@linuxprobe~]# vim addusers.sh
#!/bin/bash
read -p "Enter The Users Password : " PASSWD
for UNAME in `cat users.txt`
do
id $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "$UNAME , Already exists"
else
useradd $UNAME &> /dev/null
echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null
echo "$UNAME , Create success"
fi
done
#id命令:会显示用户以及所属群组的实际与有效 ID,若两个 ID 相同,则仅显示实际 ID,若仅指定用户名称,则显示目前用户的 ID。
#/dev/null 是一个被称作 Linux 黑洞的文件,把输出信息重定向到这个文件等同于删除数据(类似于没有回收功能的垃圾箱),可以让用户的屏幕窗口保持简洁。由于已经将多余的信息通过输出重定向符转移到了/dev/null 黑洞文件中,因此在正常情况下屏幕窗口除了“用户账户创建成功”(Create success)的提示后不会有其他内容。
-
测试主机是否在线(结合双分支if)
#创建一个主机列表文件 ipaddrs.txt [root@linuxprobe~]# vim ipaddrs.txt 149 192.168.10.10 192.168.10.11 192.168.10.12 #测试主机是否在线脚本 [root@linuxprobe~]# vim CheckHosts.sh #!/bin/bash HLIST=$(cat~/ipaddrs.txt) for IP in $HLIST do ping -c 3 -i 0.2 -W 3 $IP &> /dev/null if [ $? -eq 0 ] then echo "Host $IP is On-line." else echo "Host $IP is Off-line." fi done [root@linuxprobe~]# ./CheckHosts.sh Host 192.168.10.10 is On-line. Host 192.168.10.11 is Off-line. Host 192.168.10.12 is Off-line. #“$(命令)”是一种完全类似于第 3 章的转义字符中反引号`命令`的 Shell 操作符,效果同样是执行括号或双引号括起来的字符串中的命令
- Shell 脚本中的代码缩进格式会根据不同的语句而改变。这是由 Vim 编辑器自动完成的,用户无须进行额外操作(RHEL 7前的版本无此功能)
while条件循环语句
-
while 语句会一直循环下去,只有碰到 exit 才会结束
-
猜测数值大小
[root@linuxprobe~]# vim Guess.sh #!/bin/bash PRICE=$(expr $RANDOM % 1000) TIMES=0 echo "商品实际价格为 0-999 之间,猜猜看是多少?" while true do read -p "请输入您猜测的价格数目:" INT let TIMES++ if [ $INT -eq $PRICE ] ; then echo "恭喜您答对了,实际价格是 $PRICE" echo "您总共猜测了 $TIMES 次" exit elif [ $INT -gt $PRICE ] ; then echo "太高了!" else echo "太低了!" fi done
case条件测试语句
-
判断输入字符类型
[root@linuxprobe~]# vim Checkkeys.sh #!/bin/bash read -p "请输入一个字符,并按 Enter 键确认:" KEY case "$KEY" in [a-z]|[A-Z]) echo "您输入的是 字母。" ;; [0-9]) echo "您输入的是 数字。" ;; *) echo "您输入的是 空格、功能键或其他控制字符。" esac [root@linuxprobe~]# bash Checkkeys.sh 请输入一个字符,并按 Enter 键确认:6 您输入的是 数字。 [root@linuxprobe~]# bash Checkkeys.sh 请输入一个字符,并按 Enter 键确认:p 您输入的是 字母。 [root@linuxprobe~]# bash Checkkeys.sh 请输入一个字符,并按 Enter 键确认:^[[15~ 您输入的是 空格、功能键或其他控制字符。
4 计划任务服务程序
一次性计划任务(at命令)
如今晚 23:30 重启网站服务。一次性计划任务只执行一次,一般用于临时的工作需求。可以用 at 命令实现 这种功能,只需要写成“at 时间”的形式就行。如果想要查看已设置好但还未执行的一次性 计划任务,可以使用 at -l 命令;要想将其删除,可以使用“atrm 任务序号”
- 固定时间启动
#在今晚 23:30 自动重启网站(方式1)
[root@linuxprobe~]# at 23:30
warning: commands will be executed using /bin/sh
at> systemctl restart httpd
at> 此处请同时按下<Ctrl>+<d>组合键来结束编写计划任务
job 1 at Wed Oct 14 23:30:00 2020
[root@linuxprobe~]# at -l
1 Wed Oct 14 23:30:00 2020 a root
#在今晚 23:30 自动重启网站(方式2)
[root@linuxprobe~]# echo "systemctl restart httpd" | at 23:30
warning: commands will be executed using /bin/sh
job 2 at Wed Oct 14 23:30:00 2020
[root@linuxprobe~]# at -l
1 Wed Oct 14 23:30:00 2020 a root
2 Wed Oct 14 23:30:00 2020 a root
#使用 atrm 命令轻松删除其中一条
[root@linuxprobe~]# atrm 2
[root@linuxprobe~]# at -l
1 Wed Oct 14 23:30:00 2020 a root
- 倒计时启动
- 使用“at now +2 MINUTE”的方式进行操作,这表示 2 分钟(MINUTE)后 执行这个任务,也可以将其替代成小时(HOUR)、日(DAY)、月(MONTH)等词汇
#把计划任务写入 Shell 脚本中,当用户激活该脚本后再开始倒计时执行
[root@linuxprobe~]# at now +2 MINUTE
warning: commands will be executed using /bin/sh
at> systemctl restart httpd
at> 此处请同时按下<Ctrl>+<d>键来结束编写计划任务
job 3 at Wed Oct 14 22:50:00 2020
周期性计划任务(crontab命令)
如:每周一的凌晨 3:25 把/home/wwwroot 目录打包备份为 backup.tar.gz。使用crontab命令。
-
crontab参数设置口诀:“分、时、日、月、星期 命令。” 需要注意的是,如果有些字段 没有被设置,则需要使用星号( )占位。
-
在每周一、三、五的凌晨 3:25把/home/wwwroot 目录打包备份为 backup.tar.gz
[root@linuxprobe~]# crontab -e no crontab for root - using an empty one crontab: installing new crontab [root@linuxprobe~]# crontab -l 25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
- 多个时间段除了可以用逗号(,),还可以用减号(-)表示一段连续的时间周期如“12-15”, 则表示每月的 12~15 日,此外还可以用除号(/)表示执行任务的间隔时间(例如“*/2”表示每 隔 2 分钟执行一次任务)。
-
注意事项
-
如果在 crond 服务中需要同时包含多条计划任务的命令语句,应每行仅写一条。
-
⭐️尤其 需要注意的是,在 crond 服务的计划任务参数中,所有命令一定要用绝对路径的方式来写。如果不知道绝对路径,用 whereis 命令进行查询
[root@linuxprobe~]# whereis rm rm: /usr/bin/rm /usr/share/man/man1/rm.1.gz /usr/share/man/man1p/rm.1p.gz [root@linuxprobe~]# crontab -e crontab: installing new crontab [root@linuxprobe~]# crontab -l 25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot 0 1 * * 1-5 /usr/bin/rm -rf /tmp/*
-
在 crond 服务的配置参数中,一般会像 Shell 脚本那样以#号开头写上注释信息,这样 在日后回顾这段命令代码时可以快速了解其功能、需求以及编写人员等重要信息。
-
计划任务中的“分”字段必须有数值,绝对不能为空或是*号,而“日”和“星期” 字段不能同时使用,否则就会发生冲突。
-