Linux学习Day6:编写Shell脚本、配置计划任务

  Shell脚本命令的工作方式有两种:

  交互式(Interactive):用户每输入一条命令就立即执行。

  批处理(Batch):由用户事先编写好一个完整的Shell脚本,Shell会一次性执行脚本中诸多的命令。

一、编写简单的脚本


  一个Shell脚本主要由三部分组成:脚本声明、脚本注释、脚本命令。

  •   脚本声明:告诉系统使用哪种Shell解释器来执行该脚本,比如:#!/bin/bash
  •   脚本注释:以#开头,主要是介绍脚本的功能和某些命令
  •   脚本命令:需要被执行的Linux命令。

  Shell脚本的名称可以任意,但为了方便用户辨认,建议?#30001;?strong>.sh后缀以表示这是一个脚本文件。下面通过Vim编辑器简单编写一个Shell脚本:

[[email protected] ~]# vim example.sh
#!/bin/bash       //脚本声明
# for example by xuliang    //脚本注释
pwd        //脚本命令
ls -al

  可以通过bash命令直接运行脚本文件,?#37096;?#20197;通过输入完整路径的方式来执行,但是需要先对脚本文件添加可执行权限。

[[email protected] ~]# bash example.sh      //通过bash命令执行脚本文件
/root
total 21260
dr-xr-x---. 17 root root     4096 Feb 23 16:57 .
drwxr-xr-x. 17 root root     4096 Feb 23 10:34 ..
drwxr-xr-x.  3 root root       14 Feb 18 15:26 a
-rw-------.  1 root root     1032 Feb 18  2019 anaconda-ks.cfg
-rw-------.  1 root root     6039 Feb 23 10:57 .bash_history
---------------------省略部分输出内容------------------------------
[[email protected] ~]# ./example.sh      //通过完整路径执行脚本文件,需要可执行权限
-bash: ./example.sh: Permission denied [[email protected] ~]# chmod u+x example.sh //添加可执行权限 [[email protected] ~]# [[email protected] ~]# ./example.sh //脚本执行成功 /root total 21260 dr-xr-x---. 17 root root 4096 Feb 23 16:57 . drwxr-xr-x. 17 root root 4096 Feb 23 10:34 .. drwxr-xr-x. 3 root root 14 Feb 18 15:26 a -rw-------. 1 root root 1032 Feb 18 2019 anaconda-ks.cfg -rw-------. 1 root root 6039 Feb 23 10:57 .bash_history -------------------省略部分输出内容-----------------------------

二、接收用户的参数


  上面的脚本只能执行一些预先定义好的命令,未免太过于死板了。为了增加Shell脚本的灵活性,必须让脚本可以接收用户输入的参数。Linux系统中的Shell脚本语言已经内设了用于接受参数的变量,变量之间使用空格间隔,相关变量如下所示:

  •   $0:表示当前Shell脚本的名称;
  •   $#:表示总共有几个参数;
  •   $*:表示所有位置的参数值
  •   $1、$2、$3、$4.....:表示第N个位置的参数值。

 

  “百闻不如一见,看书不如实践”,接下来通过编写一个脚本,引用上面的变量参数来看一下实际效果:

[[email protected] ~]# vim example.sh
#!/bin/bash
# for example by xuliang
echo "当前脚本名称$0"
echo "总共有$#个参数,分别是$*"
echo "第一个参数为$1,第3为$3"
[[email protected] ~]# bash example.sh 1 2 3 4 5 6 //传入6个参数 当前脚本名称example.sh 总共有6个参数,分别是1 2 3 4 5 6 第一个参数为1,第3为3

三、判断用户的参数


  接下来学习如何处理接收到的用户参数。Shell脚本中的条件测试语法可以判断表达式是否成立,若条件成立则返回数字0,否则返回其他随机数字(一般都是返回1),条件测试语句的执行格式如下所示。切记,条件表达式两边必须要有一个空格

  按照测试对象来划分,条件测试语句可以分为4种:

  •   文件测试语句;
  •   逻辑测试语句;
  •   整数值比较语句;
  •   字符串比较语句。

  1、文件测试语句

  指使用指定条件来判断文件是否存在或权限是否满足等情况的运算符,具体参数如下所示:

运算符 作用
-d 测试文件是否为目录类型
-e 测试文件是否存在
-f 判断是否为一般文件
-r 测试当前用户是否有权限读取
-w 测试当前用户是否有权限写入
-x 测试当前用户是否有权限执行

  实验1:使用文件测试语句判断/etc/fstab是否为一个目录类型文件,然后通过Shell解释器的内设$?变量来显示上一条命令执行后的返回值。如果上一条命令执行成功,则$?变量的数值为0;反之则为一个非零值(一般都是1)。

[[email protected] ~]# [ -d /etc/fstab ]    
[[email protected] ~]# echo $?           //显示上条命令执行结果
1         //非零值表示执行失败
[[email protected] ~]# 

  实验2:使用条件测试语句判断/etc/fstab是否为一个一般文件。

[[email protected] ~]# [ -f /etc/fstab ]
[[email protected] ~]# echo $?
0        //执行成功
[[email protected] ~]# 

  2、逻辑测试语句

  指对测试结果进行逻辑分析,根据测试结果实现不同的效果。主要有3种逻辑运算符,如下所示:

运算符 作用
逻辑与(&&) 表示前面的命令执行成功后,才执行后面的命令
逻辑或(||) 表示前面的命令执行失败后,才执行后面的命令
逻辑非(!) 表示把条件测试语句中的判断结果取相反值

   实验1:判断/dev/cdrom文件是否存在,若存在则输出Exist字样。

[[email protected] ~]# [ -e /dev/cdrom ] && echo "Exist"      //逻辑与
Exist

   实验2:判?#31995;?#21069;登录的用户是否为管理员身份:

[[email protected] ~]# su - linuxprobe     //切换至linuxprobe用户
Last login: Mon Feb 24 10:26:51 BNT 2020 on pts/1
[[email protected] ~]$
[[email protected] ~]$ [ $USER = root ] || echo "not root"       //逻辑或
not root

   实验3:判?#31995;?#21069;登录用户为非管理员身份:

[[email protected] ~]$ su - root    //切换至root用户
Password: 
Last login: Mon Feb 24 11:13:05 BNT 2020 on pts/1
[[email protected] ~]# [ ! $USER = root ] || echo "It's root"        //逻辑非
It's root

  实验4:判?#31995;?#21069;登录的用户,若是普通用户则输出“user”,若是管理员用户则输出“root” 

[[email protected] ~]# [ ! $USER = root ] && echo "user" || echo "It's root"
It's root     //root用户
[[email protected] ~]# su - linuxprobe      //切换至linuxprobe用户
Last login: Mon Feb 24 14:41:29 BNT 2020 on pts/1
[[email protected] ~]$ [ ! $USER = root ] && echo "user" || echo "It's root"
user     //普通用户

  3、整数比较运算符

  仅仅是对数字的操作,即运算符的两边必须是数字,不能将数字与字符串、文件等内容一起操作,而且一定要使用规范的整数比较运算符来进行操作。整数比较运算符如下所示:

运算符 作用
-eq 是否等于
-ne 是否不等于
-gt 是否大于(greater than)
-lt 是否小于(less than)
-le 是否等于或小于
-ge 是否大于或等于

  实验:判?#31995;?#21069;主机空闲内存是否小于1024M,若小于1024M,则输出“Insufficient Memory”的字样。

[[email protected] ~]$ FreeMem=`free -m | grep Mem: | awk '{print $4}'`    //获取当前主机空闲内存值,注意赋值号的两边不能有空格
[[email protected] ~]$ 
[[email protected] ~]$ [ $FreeMem -lt 1024 ] && echo "Insuficient Memory"  
Insuficient Memory 

  4、字符串比较语句

  用于判断测试字符串是否为空值,或两个字符串是否相同,常见的字符串运算符如下所示:

运算符 作用
= 比较字符串的内容是否相同
!= 比较字符串的内容是否不同
-z 判断字符串的内容是否为空

   实验1:判断变量String是否空值,即判断是否定义了该变量。

[[email protected] ~]$ [ -z $String ]
[[email protected] ~]$ echo $?
0     

  实验2:判?#31995;?#21069;LANG环境变量值是否为“en.US”。

[[email protected] ~]$ echo $LANG
en_US.UTF-8
[[email protected] ~]$ [ $LANG = "en.US" ] || echo "Not en.US"
Not en.US

 四、流程控制语句


  在Shell脚本中,我们可以通过if、for、while、case这4中流程控制语句来编写难度更大、功能更强的脚本,来匹配实际的生产需求。

  1、if条件测试语句

  if条件测试语句分为单?#31181;?#32467;构、双?#31181;?#32467;构、多?#31181;?#32467;构,下面逐一进行介绍:

  (1)单?#31181;?#32467;构

  由if、then、fi关键?#39318;?#25104;,只有在判断条件成立之后才执行预设的命令,相当于口语的“如果........那么........”。语法格式如下所示:

   实验:判断/root/test目录文件是否存在,如存在则结束Shell脚本,否则创建该目录。

[[email protected] ~]# vim mkcdrom.sh
#!/bin/bash
DIR="/root/test"
if [ ! -e $DIR ]
  then
    mkdir -p $DIR
fi

[[email protected] ~]# bash mkcdrom.sh     //执行脚本
[[email protected] ~]# ls -d /root/test/    //检查目录是否创建成功
/root/test/

  (2)多?#31181;?#32467;构

  由if、then、else、fi关键?#39318;?#25104;,它进行一次条件匹配判断,若匹配成功,则执行预设的命令,否则去执行匹配失败时的预设命令。语法格式如下所示:

   实验:用户自行输入IP地址,并判断该主机是否在线。

[[email protected] ~]# vim chkhost.sh
#!/bin/bash
ping -c 3 -i 0.2 -w 3 $1 &> /dev/null    ###其中参数表示ping3次、每次间隔0.2秒、等待超?#31508;?#38388;为3秒
if [ $? -eq 0 ]
  then
    echo "Host $1 is On-line"
  else
    echo "Host $1 is Off-line"
fi

[[email protected] ~]# bash chkhost.sh 192.168.134.10   
Host 192.168.134.10 is On-line     //主机在线
[[email protected] ~]# bash chkhost.sh 192.168.134.20
Host 192.168.134.20 is Off-line    //主机不在线
[[email protected] ~]# 

   (3)多?#31181;?#32467;构

  if、then、else、elif、fi关键?#39318;?#25104;,它进行多次条件匹配判断,这么多次判断中的任一项匹配成功都会执行相应的预设命令,相当于口语的“如果.....那么......如果.......那么.....”。多?#31181;?#35821;句可以多次嵌套,语法格式如下所示:

   实验:判断用户输入的分数在哪个区间,然后输出Excellent、Pass、Fail等提示信息。

[[email protected] ~]# vim chkscore.sh
#!/bin/bash
read -p "Enter your score(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ] then echo "Excellent" elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] then echo "Pass" else echo "Fail" fi [[email protected] ~]# bash chkscore.sh Enter your score(0-100):88 Excellent [[email protected] ~]# bash chkscore.sh Enter your score(0-100):73 Pass [[email protected] ~]# bash chkscore.sh Enter your score(0-100):60 Fail

   2、for条件循环语句

  for循环语句允许脚本一次性读取多个信息,然后逐一对信息进行操作处理。当要处理有范围的数据时,使用for循环语句再适合不过了,其语法格式如下所示:

   实验1:从列表文件中读取多个用户名,然后为其逐一创建用户账号并设置密码。

[[email protected] ~]# vim users.txt     //首先创建包含用户名称的文件
zhangsan
lisi
wangwu
zhaoliu

[[email protected] ~]# vim CreateUser.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 "Already Exists" else useradd $UNAME &> /dev/null ##添加用户 echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null if [ $? -eq 0 ] then echo "$UNAME create success" else echo "$UNAME create failure" fi fi done [[email protected] ~]# bash CreateUser.sh //执行脚本 Enter the Users Password:123456 zhangsan create success lisi create success wangwu create success zhaoliu create success [[email protected] ~]#

  补充:假如用户名文件users.txt内容写成以下形式(即所有用户名写成一行,用空格分开),CreateUser.sh脚本同样执行成功。

[[email protected] ~]# cat users.txt 
andy barry carl    
[[email protected] ~]# 
[[email protected] ~]# bash CreateUser.sh 
Enter the Users Password:123456
andy create success
barry create success
carl create success

   实验2:读取文件中的主机列表,然后逐个测试这些主机是否在线。

[[email protected] ~]# vim ipaddrs.txt      //创建主机列表文件
192.168.134.10
192.168.134.20
192.168.134.30
192.168.134.40

[[email protected] ~]# vim chkhosts.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 "$IP is On-line" else echo "$IP is Off-line" fi done [[email protected] ~]# bash chkhosts.sh //运行脚本 192.168.134.10 is On-line 192.168.134.20 is Off-line 192.168.134.30 is Off-line 192.168.134.40 is Off-line

  3、while条件循环语句

  while条件语句是一种让脚本根据某些条件来重复执行命令的语句,它的循?#26041;?#26500;往往在执行前并不确定最终执行的次数,它通过判断条件测试的真假来决定是否继续执行命令,条件为真就继续执行,为假就结束循环,语法结构如下所示:

   实验:编写一个猜数的脚本,若用户输入的数值与脚本随机生成的数值一致,则结束游戏。

[[email protected] ~]# vim guess.sh
#!/bin/bash
PRICE=$(expr $RANDOM % 1000)      ##获取一个1000?#38405;?#30340;随机数
TIMES=0
echo "商品的实际价格为0-999之间,请猜猜看是多少?"
while true
do
  read -p "请输入你猜测的价格:" INT
  let TIMES++                        ##TIMES变量自增1
  if [ $INT -eq $PRICE ] ; then
    echo "恭喜你猜对啦,实际价格为 $PRICE"
    echo "您总共猜了 $TIMES 次"
    exit 
  elif [ $INT -gt $PRICE ] ; then
    echo "您猜高了"
  else
    echo "您猜低了"
  fi
done

[[email protected] ~]# bash guess.sh 
商品的实际价格为0-999之间,请猜猜看是多少?
请输入你猜测的价格:500
您猜低了
请输入你猜测的价格:800
您猜高了
-------------------省略部分内容---------------------
您猜高了
请输入你猜测的价格:516
您猜低了
请输入你猜测的价格:517
恭喜你猜对啦,实际价格为 517
您总共猜了 14

   4、case条件测试语句

  case语句是在多个范围内匹配数据,若匹配成功则执行相关命令并结束整个条件测试;如果数据在所列的范围内,则会去执行星号(*)中所定义的默认命令,语法结构如下所示:

   实验:判断用户输入的数据是字母、数?#21482;?#26159;其他字符。

[[email protected] ~]# cat chkkeys.sh 
#!/bin/bash
read -p "请输入一个字符:" KEY
case "$KEY" in
  [a-z]|[A-Z])
    echo "您输入的是字母"
    ;;
  [0-9])
    echo "您输入的是数字"
    ;;
  *)
    echo "您输入的是特殊字符"
esac
[[email protected] ~]# bash chkkeys.sh 
请输入一个字符:2
您输入的是数字
[[email protected] ~]# bash chkkeys.sh 
请输入一个字符:s
您输入的是字母
[[email protected] ~]# bash chkkeys.sh 
请输入一个字符:`
您输入的是特殊字符
[[email protected] ~]# 

  补充:上述关于检查字符的脚本是有缺陷的,当用户输入两位及以上的数?#21482;?#23383;母时,会提示输入的是特殊字符。

[[email protected] ~]# bash chkkeys.sh 
请输入一个字符:12
您输入的是特殊字符

  解决方法:修改匹配条件,如下所示。但是当输入三位数字时,依然提示输入的是特殊字符,需要继续修改匹配条件。所以该方法只能算打补丁,不能算真正的解决方法。

[[email protected] ~]# cat chkkeys.sh 
#!/bin/bash
read -p "请输入一个字符:" KEY
case "$KEY" in
  [a-z]|[A-Z])
    echo "您输入的是字母"
    ;;
  [0-9]|[0-9][0-9])      ##匹配一位或两位数字
    echo "您输入的是数字"
    ;;
  *)
    echo "您输入的是特殊字符"
esac
[[email protected] ~]# bash chkkeys.sh 
请输入一个字符:34
您输入的是数字

 四、计划任务服务程序


  在实际的运维工作中,需要在指定的时间段自动启动或停止某些服务或命令,从而实现运维的自动化。接下来介绍如何设置服务器的计划任务服务,把周期性、规律性的工作交给系统自动完成。

  计划任务分为一次性任务长期性任务,如下所示:

  •   一次性计划任务:比如今晚11点30分开启网站服务。
  •   长期性计划任务:比如每周一的凌晨3点30分把/home/www目录的文件打包备份为backup.tar.gz。

  1、一次性计划任务

  顾名思义,一次性计划任务只执行一次,一般用于满足临时的工作需求,可以用at命令实现这种功能,相关功能如下所示:

  •   at 时间  :表示创建一个一次性计划任务。
  •   at -l  :表示查看已经设置好但未执行的一次性计划任务。
  •   atrm 任务序号  : 表示?#22659;?#19968;个一次性计划任务。

   实验1:设置在今天11:24重启系统主机

[[email protected] ~]# at 11:24
at> reboot
at> <此处按下Ctrl+d组合键来结束编写计划任务>
job 2 at Sat Feb 29 11:24:00 2020
[[email protected] ~]# at -l    
2    Sat Feb 29 11:24:00 2020 a root
[[email protected] ~]# 

  实验2:at命令默认采用交互式的方式,接下使用非交互式的方法创建一个一次性计划任务。

[[email protected] ~]# echo "systemctl restart network" | at 11:35
job 4 at Sat Feb 29 11:35:00 2020
[[email protected] ~]# 
[[email protected] ~]# at -l
4    Sat Feb 29 11:35:00 2020 a root    //其中"4"就是任务序号

  实验3:?#22659;?#19968;个一次性计划任务。

  2、长期性计划任务

  Linux系统中默认启动的crond服务能够周期性地、有规律地执行某些具体的任务,相关地命令如下所示:

命令 作用
crontab -e 创建、编辑计划任务
crontab -l 查看当前地计划任务
crontab -r ?#22659;?#26576;条计划任务
crontab -u 编辑他人的计划任务(必须是root身份)

   使用crond服务设置计划任务时,语法格式为“分  时  日  月  星期  命令”。需要注意的是,如果有些字段没有设置,则必须使用星号(*)占位,如下图所示:

   使用crond设置任务的参数字段说明,如下表所示:

字段 说明
取值为0~59的整数
取值为0~23的任意整数
取值为0~31的任意整数
取值1~12的任意整数
星期 取值0~7的任意整数,其中0与7均为星期日
命令 要执行的命令或脚本(必须要用绝对路径来写)

  实验1:设置在每周一、三、五的凌晨3点25分,使用tar命令打包网站的数据。

[[email protected] ~]# crontab -e      //默认进入Vim编辑器界面
no crontab for root - using an empty one
crontab: installing new crontab
[[email protected] ~]# crontab -l
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
[[email protected] ~]# 

  补充说明:这里补充几点关于时间周期的设置,如下所示:

  •   用逗号(,)来表示多个时间段,如“星期”字段“1,3,5”表示周一、周三和周五。
  •   用减号(-)来表示一段连续的时间周期,如“月”字段“8-12”表示8~12月。
  •   用除号(/)来表示执行任务的间隔时间,如“分”字段“*/2”表示每隔2?#31181;?#25191;行一次任务。

   crond服务可?#22253;?#21547;多条计划任务,注意每条计划任务占一行。比如我们要增加一条计划任务,它的功能是每周一至周五的凌晨1点自动清空/tmp目录内的所有文件。

[[email protected] ~]# whereis rm     //使用whereis命令查询绝对路径
rm: /usr/bin/rm /usr/share/man/man1/rm.1.gz /usr/share/man/man1p/rm.1p.gz
[[email protected] ~]# 
[[email protected] ~]# crontab -e
crontab: installing new crontab
[[email protected] ~]# 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/*

  注意事项:

  (1)在Vim编辑器中配置计划任务时,可以以#号开头写上注释信息。

  (2)计划任务中的“分”字段必须有数值,绝对不能为空或*号。

  (3)“日”和“星期”字段不能同?#31508;?#29992;,否则会发生冲突。

 

 

 

posted @ 2020-02-23 17:22  黑色幽默2020  阅读(...)  评论(...编辑  收藏
三剑客和女王APP
245000皇冠比分走地赔 11选5河南最新开奖今天 北京麻将抓牌顺序 江西十一选五前三直 炒股软件排名 网上打麻将微信发红包 bfb游戏理财平台 手机比分捷报 安徽体彩十一选五一 免费炒股软件排名 球探网一篮球即时比分 山东十一选五推荐 免费下载东北麻将 排列三太湖钓翁三字诀字谜 安徽十一选五 足球捷报比分推荐网