Shell编程——数组

数组

1、Shell数组介绍

1.1、为什么会产生Shell数组

通常在开发Shell脚本时,定义变量采用的形式为"a=1;b=2; c=3"​,可如果有多个变量呢?这时再逐个地定义就会很费劲,并且要是有多个不确定的变量内容,也会难以进行变量定义,此外,快速读取不同变量的值也是一件很痛苦的事情,于是数组就诞生了,它就是为了解决上述问题而出现的。

1.2、什么是Shell数组

如果读者有过其他语言的编程经历,那么想必会熟悉数组的概念。简单地说, Shell的数组就是一个元素集合,它把有限个元素(变量或字符内容)用一个名字来命名,然后用编号对它们进行区分。这个名字就称为数组名,用于区分不同内容的编号就称为数组下标。组成数组的各个元素(变量)称为数组的元素,有时也称为下标变量。

有了Shell数组之后,就可以用相同名字来引用一系列变量及变量值了,并通过数字(索引)来识别使用它们。在很多场合中,使用数组可以缩短和简化程序开发。

2、Shell数组的定义与增删改查

2.1、Shell数组的定义

Shell数组的定义有多种方法,列举如下。

方法1:用小括号将变量值括起来赋值给数组变量,每个变量值之间要用空格进行分隔。

语法如下:

bash 复制代码
array=(value1 value2 value3 ... )

此为常用定义方法,需要重点掌握。

示例如下:

bash 复制代码
[root@oldboy ~]# array=(1 2 3)     #<==用小括号将数组内容赋值给数组变量,数组元素用"空格"分隔开。
[root@oldboy ~]# echo ${array[*]} #<==输出上面定义的数组的所有元素值,注意语法。
1 2 3

方法2:用小括号将变量值括起来,同时采用键值对的形式赋值。

语法如下:

bash 复制代码
array=([1]=one [2]=two [3]=three)

此种方法为key-value键值对的形式,小括号里对应的数字为数组下标,等号后面的内容为下标对应的数组变量的值,此方法比较复杂,不推荐使用。

示例如下:

bash 复制代码
[root@oldboy scripts]# array=([1]=one [2]=two [3]=three)
[root@oldboy scripts]# echo ${array[*]} #<==输出上面定义的数组的所有元素值。
one two three
[root@oldboy scripts]# echo ${array[1]} #<==输出上面定义的数组的第一个元素值。
one
[root@oldboy scripts]# echo ${array[2]} #<==输出上面定义的数组的第二个元素值。
two
[root@oldboy scripts]# echo ${array[3]} #<==输出上面定义的数组的第三个元素值。
three

方法3:通过分别定义数组变量的方法来定义。

语法如下:

bash 复制代码
array[0]=a; array[1]=b; array[2]=c

此种定义方法比较麻烦,不推荐使用。

示例如下:

bash 复制代码
[root@oldboy scripts]# array[0]=a
[root@oldboy scripts]# array[1]=b
[root@oldboy scripts]# array[2]=c
[root@oldboy scripts]# echo ${array[0]}
a

方法4:动态地定义数组变量,并使用命令的输出结果作为数组的内容。

语法为:

bash 复制代码
array=($(命令))

或:

bash 复制代码
array=(`命令`)

示例如下:

bash 复制代码
[root@oldboy scripts]# mkdir /array/ -p
[root@oldboy scripts]# touch /array/{1..3}.txt
[root@oldboy scripts]# ls -l /array/
总用量 0
-rw-r--r-- 1 root root 0 9月    6 09:38 1.txt
-rw-r--r-- 1 root root 0 9月    6 09:38 2.txt
-rw-r--r-- 1 root root 0 9月    6 09:38 3.txt
[root@oldboy scripts]# array=($(ls /array))
[root@oldboy scripts]# echo ${array[*]}
1.txt 2.txt 3.txt

说明:还可以使用declare -a array来定义数组类型,但是比较少这样用。

2.2、Shell数组的打印及输出

2.2.1、打印数组元素

此为常用知识点,需要重点掌握。示例如下:

bash 复制代码
[root@oldboy scripts]# array=(one two three)
[root@oldboy scripts]# echo ${array[0]}
#<==打印单个数组元素用${数组名[下标]},当未指定数组下标时,数组的下标将从0开始。
one
[root@oldboy scripts]# echo ${array[1]}
two
[root@oldboy scripts]# echo ${array[2]}
three
[root@oldboy scripts]# echo ${array[*]} #<==使用*或@可以得到整个数组的内容。
one two three
[root@oldboy scripts]# echo ${array[@]}  #<==使用*或@可以得到整个数组的内容。
one two three
2.2.2、打印数组元素的个数

此为常用知识点,需要重点掌握。示例如下:

bash 复制代码
[root@oldboy scripts]# echo ${array[*]}  #<==使用*或@可以得到整个数组内容。
one two three
[root@oldboy scripts]# echo ${#array[*]} #<==用${#数组名[@或*]}可以得到数组的长度,这和前文讲解的变量子串知识是一样的,因为数组也是变量,只不过是特殊的变量,因此变量的子串替换等知识也适合于数组。
3
[root@oldboy scripts]# echo ${array[@]}   #<==使用*或@可以得到整个数组内容。
one two three
[root@oldboy scripts]# echo ${#array[@]} #<==用${#数组名[@或*]}可以得到数组的长度,这和前文讲解的变量子串知识是一样的,因为数组也是变量,只不过是特殊的变量,因此变量的子串替换等知识也适合于数组。
3
2.2.3、数组赋值

此知识不常用,了解即可。可直接通过"数组名下标​"对数组进行引用赋值,如果下标不存在,则自动添加一个新的数组元素,如果下标存在,则覆盖原来的值。

示例如下:

bash 复制代码
[root@oldboy scripts]# array=(one two three)
[root@oldboy scripts]# echo ${array[*]}
one two three
[root@oldboy scripts]# array[3]=four      #<==增加下标为3的数组元素。
[root@oldboy scripts]# echo ${array[*]}
one two three four
[root@oldboy scripts]# array[0]=oldboy
[root@oldboy scripts]# echo ${array[*]}
oldboy two three four
[root@oldboy scripts]# array[0]=oldboy    #<==修改数组元素。
[root@oldboy scripts]# echo ${array[@]}
oldboy 2 3 4
2.2.4、数组的删除

因为数组本质上还是变量,因此可通过"unset数组下标​"清除相应的数组元素,如果不带下标,则表示清除整个数组的所有数据。

示例如下:

bash 复制代码
[root@oldboy scripts]# echo ${array[*]}
oldboy two three four
[root@oldboy scripts]# unset array[1]     #<==取消下标为1的数组元素。
[root@oldboy scripts]# echo ${array[*]}  #<==打印输出后发现数组元素"two"不见了。
oldboy three four
[root@oldboy scripts]# unset array        #<==删除整个数组。
[root@oldboy scripts]# echo ${array[*]}
#<==没有任何内容了。
2.2.5、数组内容的截取和替换

这里和前文变量子串的替换是一样的,因为数组是特殊的变量。数组元素部分的内容截取的示例如下:

bash 复制代码
[root@oldboy scripts]# array=(1 2 3 4 5)
[root@oldboy scripts]# echo ${array[@]:1:3}     #<==截取1号到3号数组元素。
2 3 4
[root@oldboy scripts]# array=($(echo {a..z}))   #<==将变量的结果赋值给数组变量。
[root@oldboy scripts]# echo ${array[@]}
a b c d e f g h i j k l m n o p q r s t u v w x y z
[root@oldboy scripts]# echo ${array[@]:1:3}     #<==截取下标为1到3的数组元素。
b c d
[root@oldboy scripts]# echo ${array[@]:0:2}     #<==截取下标为0到2的数组元素。
a b

替换数组元素部分内容的代码如下:

bash 复制代码
[root@oldboy scripts]# array=(1 2 3 1 1)
[root@oldboy scripts]# echo ${array[@]/1/b}     #<==把数组中的1替换成b,原数组未被修改,和sed很像。
b 2 3 b b

提示:调用方法为${数组名@或*/查找字符/替换字符},该操作不会改变原先数组的内容,如果需要修改,可以参考上面的例子,重新定义数组。

删除数组元素部分内容的代码如下:

bash 复制代码
[root@oldboy scripts]# array=(one two three four five)
[root@oldboy scripts]# echo ${array[@]}
one two three four five
[root@oldboy  scripts]#  echo  ${array[@]#o*}    #<==从左边开始匹配最短的数组元素,并删除。
one two three four five
[root@oldboy  scripts]#  echo  ${array1[@]##o*}  #<==从左边开始匹配最长的数组元素,并删除。
two three four five
[root@oldboy  scripts]#  echo  ${array[@]%f*}    #<==从右边开始匹配最短的数组元素,并删除。
one two three
[root@oldboy  scripts]#  echo  ${array[@]%%f*}   #<==从右边开始匹配最长的数组元素,并删除。
one two three

提示:数组也是变量,因此也适合于前面讲解过的变量的子串处理的功能应用。数组的其他相关知识可通过man bash命令然后搜"Arrays"来了解。

3、Shell数组脚本开发实践

范例:使用循环批量输出数组的元素。

方法1:通过C语言型的for循环语句打印数组元素。

bash 复制代码
[root@oldboy scripts]# cat 13_1_1.sh
#! /bin/sh
array=(1 2 3 4 5)
for((i=0; i<${#array[*]}; i++))  #<==从数组的第一个下标0开始,循环数组的所有下标。
do
    echo ${array[i]}               #<==打印数组元素。
done

输出结果如下:

bash 复制代码
[root@oldboy scripts]# sh 13_1_1.sh
1
2
3
4
5

方法2:通过普通for循环语句打印数组元素。

bash 复制代码
[root@oldboy scripts]# cat 13_1_2.sh
#! /bin/sh
array=(1 2 3 4 5)
for n in ${array[*]} #<==${array[*]}表示输出数组的所有元素,相当于列表数组元素。
do
    echo $n            #<==这里就不是直接去数组里取元素了,而是取变量n的值。
done

输出结果同方法1,此处略过。

方法3:使用while循环语句打印数组元素。

bash 复制代码
[root@oldboy scripts]# cat 13_1_3.sh
#! /bin/sh
array=(1 2 3 4 5)
i=0
while ((i<${#array[*]}))
do
    echo ${array[i]}
    ((i++))
done

输出结果同方法1,此处略过。

范例:通过竖向列举法定义数组元素并批量打印。

bash 复制代码
[root@oldboy scripts]# cat 13_2_1.sh
#! /bin/sh
array=(    #<==对于元素特别长的情况,例如URL地址,将其竖向列出来看起来会更舒服和规范。
oldboy
oldgirl
xiaoting
bingbing
)
for ((i=0; i<${#array[*]}; i++))
do
    echo "This is num $i, then content is ${array[$i]}"
done
echo ----------------------
echo "array len:${#array[*]}"

输出结果如下:

bash 复制代码
[root@oldboy scripts]# sh 13_2_1.sh
This is num 0, then content is oldboy
This is num 1, then content is oldgirl
This is num 2, then content is xiaoting
This is num 3, then content is bingbing
----------------------
array len:4

范例:将命令结果作为数组元素定义并打印。

准备数据:

bash 复制代码
[root@oldboy scripts]# mkdir -p /array/
[root@oldboy scripts]# touch /array/{1..3}.txt
[root@oldboy scripts]# ls /array/
1.txt   2.txt   3.txt

以下为开发脚本:

bash 复制代码
[root@oldboy scripts]# cat 13_3_1.sh
#! /bin/bash
dir=($(ls /array))                 #<==把ls /array命令结果放进数组里。
for ((i=0; i<${#dir[*]}; i++))   #<==${#dir[*]}为数组的长度。
do
   echo "This is NO.$i, filename is ${dir[$i]}"
done

输出结果如下:

bash 复制代码
[root@oldboy scripts]# sh 13_3_1.sh
This is NO.0, filename is 1.txt
This is NO.1, filename is 2.txt
This is NO.2, filename is 3.txt

4、Shell数组的重要命令

4.1、定义命令

静态数组:

bash 复制代码
array=(1 2 3)

动态数组:

bash 复制代码
array=($(ls))

为数组赋值:

bash 复制代码
array[3]=4

4.2、打印命令

打印所有元素:

bash 复制代码
array=(1 2 3)

打印数组长度:

bash 复制代码
${#array[@]}或${#array[*]}

打印单个元素:

bash 复制代码
${array[i]} #<==i是数组下标。

4.3、循环打印的常用基本语法

bash 复制代码
#! /bin/sh
arr=(
    10.0.0.11
    10.0.0.22
    10.0.0.33
)
#<==C语言for循环语法
for ((i=0; i<${#arr[*]}; i++))
do
    echo "${arr[$i]}"
done
echo ---------------
#<==普通for循环语法
for n in ${arr[*]}
do
    echo "$n"
done

5、Shell数组相关面试题及高级实战案例

范例:利用bash for循环打印下面这句话中字母数不大于6的单词(某企业面试真题)​。

bash 复制代码
I am oldboy teacher welcome to oldboy training class

解答思路具体如下。

  • 1)先把所有的单词放到数组里,然后依次进行判断。命令如下:

    bash 复制代码
    array=(I am oldboy teacher welcome to oldboy training class)
  • 2)计算变量内容的长度,这在前文已经讲解过了。常见方法有4种:

    bash 复制代码
    [root@oldboy scripts]# char=oldboy
    [root@oldboy scripts]# echo $char|wc -L
    6
    [root@oldboy scripts]# echo ${#char}
    6
    [root@oldboy scripts]# expr length $char
    6
    [root@oldboy scripts]# echo $char|awk '{print length($0)}'
    6

方法1:通过数组方法来实现。

bash 复制代码
arr=(I am oldboy teacher welcome to oldboy training class)
for ((i=0; i<${#arr[*]}; i++))
do
    if [ ${#arr[$i]} -lt 6 ]
      then
        echo "${arr[$i]}"
    fi
done
echo -----------------------
for word in ${arr[*]}
do
    if [ `expr length $word` -lt 6 ]; then
        echo $word
    fi
done

方法2:使用for循环列举取值列表法。

bash 复制代码
for word in I am oldboy teacher welcome to oldboy training class
#<==看起来有点初级吧。
do
    if [ `echo $word|wc -L` -lt 6 ]; then
        echo $word
    fi
done
chars="I am oldboy teacher welcome to oldboy training class"
#<==定义字符串可以。
for word in $chars
do
    if [ `echo $word|wc -L` -lt 6 ]; then
        echo $word
    fi
done

方法3:通过awk循环实现。

bash 复制代码
[root@oldboy scripts]# chars="I am oldboy teacher welcome to oldboy training class"
[root@oldboy scripts]# echo $chars|awk '{for(i=1; i<=NF; i++) if(length($i)<=6) print $i}'

几种方法的输出结果统一为:

bash 复制代码
I
am
oldboy
to
oldboy
class

范例:批量检查多个网站地址是否正常。

要求:

解题思路:

  • 1)把URL定义成数组,形成函数。
  • 2)编写URL检查脚本函数,传入数组的元素,即URL。
  • 3)组合实现整个案例,编写main主函数(即执行函数),每隔10秒检查一次。

下面的参考答案采用了Shell数组的方法,同时检测多个URL是否正常,并给出专业的展示效果​:

bash 复制代码
[root@oldboy scripts]# cat 10_7_2.sh
#! /bin/bash
# this script is created by oldboy.
# e_mail:31333741@qq.com
# function:case example
# version:1.3
. /etc/init.d/functions

check_count=0
url_list=(                           #<==定义检测的URL数组,包含多个URL地址。
http://blog.oldboyedu.com
http://blog.etiantian.org
http://oldboy.blog.51cto.com
http://10.0.0.7
)

function wait()                      #<==定义3,2,1倒计时函数。
{
    echo -n '3秒后,执行检查URL操作.';
    for ((i=0; i<3; i++))
    do
        echo -n "."; sleep 1
    done
    echo
}
function check_url()                 #<==定义检测URL的函数。
{
    wait                             #<==执行倒计时函数。
    for ((i=0; i<`echo ${#url_list[*]}`; i++)) #<==循环数组元素。
    do
        wget -o /dev/null -T 3 --tries=1 --spider ${url_list[$i]} >/dev/null 2>&1
                                     #<==检测是否可以访问数组元素的地址。
        if [ $? -eq 0 ]              #<==如果返回值为0,则表示访问成功。
          then
            action "${url_list[$i]}" /bin/true #<==优雅地显示成功结果。
        else
            action "${url_list[$i]}" /bin/false #<==优雅地显示失败结果。
        fi
    done
    ((check_count++))                                  #<==检测次数加1。
}
main(){                                                #<==定义主函数。
    while true                                         #<==开启一个持续循环。
    do
        check_url                                      #<==加载检测url的函数。
        echo "-------check count:${check_count}---------"
        sleep 10                                       #<==间歇10秒。
    done
}
main                                                   #<==调用主函数运行程序。

执行结果如图所示。

提示:实际使用时,一些基础的函数脚本(例如,加颜色的函数)是放在函数文件里的(例如,放在/etc/init.d/functions里)​,与执行的脚本内容部分分离,这样看起来会更清爽,大型的语言程序都是这样开发的。

范例:开发一个守护进程脚本,每30秒监控一次MySQL主从复制是否异常(包括不同步及延迟)​,如果有异常,则发送短信报警,并发送邮件给管理员存档(此为生产实战案例)​。

提示:如果没主从复制的环境,可以把下面的文本放到文件里读取来模拟主从复制的状态。

bash 复制代码
*************************** 1. row ***************************
                Slave_IO_State: Waiting for master to send event
                    Master_Host: 10.0.0.51
                    Master_User: rep
                    Master_Port: 3306
                  Connect_Retry: 60
                Master_Log_File: mysql-bin.000013
          Read_Master_Log_Pos: 502547
                Relay_Log_File: relay-bin.000013
                  Relay_Log_Pos: 251
        Relay_Master_Log_File: mysql-bin.000013
              Slave_IO_Running: Yes  #<==IO线程状态必须为Yes。
            Slave_SQL_Running: Yes  #<==SQL线程状态必须为Yes。
                Replicate_Do_DB:
          Replicate_Ignore_DB: mysql
            Replicate_Do_Table:
        Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                    Last_Errno: 0
                    Last_Error:
                  Skip_Counter: 0
            Exec_Master_Log_Pos: 502547
                Relay_Log_Space: 502986
                Until_Condition: None
                Until_Log_File:
                  Until_Log_Pos: 0
            Master_SSL_Allowed: No
            Master_SSL_CA_File:
            Master_SSL_CA_Path:
                Master_SSL_Cert:
              Master_SSL_Cipher:
                Master_SSL_Key:
          Seconds_Behind_Master: 0    #<==和主库比较同步延迟的秒数,这个参数很重要。
Master_SSL_Verify_Server_Cert: No
                  Last_IO_Errno: 0
                  Last_IO_Error:
                Last_SQL_Errno: 0
                Last_SQL_Error:

解题思路:

  • 1)判断主从复制是否异常,主要是检测如下参数对应的值。

    bash 复制代码
    Slave_IO_Running: Yes  #<==IO线程状态必须为Yes。
    Slave_SQL_Running: Yes   #<==SQL线程状态必须为Yes。
    Seconds_Behind_Master: 0     #<==和主库比较同步延迟的秒数,这个参数很重要。
  • 2)读取状态数据或状态文件,然后取出对应的值,和正确的值进行比对,如果不符合,则表示存在故障,即调用报警脚本报警。

  • 3)如果想要更专业,还可以在主从不同步时,查看相应的错误号,判断对应的错误号以自动修复主从复制故障(也可以通过在配置文件里的配置参数来实现自动忽略故障)。

以下为参考答案。

首先给出模拟数据(注意,使用时要去掉中文注释)​。

bash 复制代码
[root@oldboy scripts]# cat slave.log
*************************** 1. row ***************************
                Slave_IO_State: Waiting for master to send event
                    Master_Host: 10.0.0.51
                    Master_User: rep
                    Master_Port: 3306
                  Connect_Retry: 60
                Master_Log_File: mysql-bin.000013
          Read_Master_Log_Pos: 502547
              Relay_Log_File: relay-bin.000013
                Relay_Log_Pos: 251
      Relay_Master_Log_File: mysql-bin.000013
            Slave_IO_Running: Yes
          Slave_SQL_Running: Yes
            Replicate_Do_DB:
        Replicate_Ignore_DB: mysql
          Replicate_Do_Table:
      Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
                  Last_Errno: 0
                  Last_Error:
                Skip_Counter: 0
        Exec_Master_Log_Pos: 502547
            Relay_Log_Space: 502986
            Until_Condition: None
              Until_Log_File:
                Until_Log_Pos: 0
          Master_SSL_Allowed: No
          Master_SSL_CA_File:
          Master_SSL_CA_Path:
            Master_SSL_Cert:
          Master_SSL_Cipher:
              Master_SSL_Key:
      Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
              Last_SQL_Errno: 0
              Last_SQL_Error:

然后开发脚本,有多种方法,下面分别给出各个参考方法。

方法1:

bash 复制代码
[root@oldboy scripts]# awk -F ':' '/_Running|_Behind/{print $NF}' slave.log
#<==获取所有复制相关的状态值,脚本里使用slave.log时注意完全路径。
Yes
Yes
0
[root@oldboy scripts]# cat 13_6_1.sh
count=0
status=($(awk -F ':' '/_Running|_Behind/{print $NF}' slave.log))
                                #<==获取所有复制相关的状态值赋并值给数组status。
for((i=0; i<${#status[*]}; i++)) #<==循环数组元素。
do
  if [ "${status[${i}]}" ! = "Yes" -a "${status[${i}]}" ! = "0" ]
  #<==如果数组元素值不为Yes或0中的任意一个,那就表示复制出故障了。
    then
      let count+=1             #<==错误数加1。
  fi
done
if [ $count -ne 0 ]; then #<==只要错误数不等于0,就表示状态值肯定是有问题的。
  echo "mysql replcation is failed"   #<==提示复制出现问题。
else
  echo "mysql replcation is sucess"   #<==否则提示复制正常。
fi

测试结果如下:

bash 复制代码
[root@oldboy scripts]# sh 13_6_1.sh
mysql replcation is sucess
[root@oldboy  scripts]#  sed  -i  's#Slave_IO_Running:  Yes#Slave_IO_Running:
No#g' slave.log                                  #<==模拟IO线程故障。
[root@oldboy scripts]# sh 13_6_1.sh
mysql replcation is failed

方法2:本方法和方法1实现的功能差不多,但是开发手法更高大上一些。

bash 复制代码
[root@oldboy scripts]# cat 13_6_2.sh
#! /bin/bash
CheckDb(){
    status=($(awk -F ':' '/_Running|_Behind/{print $NF}' slave.log))
    for((i=0; i<${#status[*]}; i++))
    do
        count=0
        if [ "${status[${i}]}" ! = "Yes" -a "${status[${i}]}" ! = "0" ]
          then
            let count+=1
        fi
    done
    if [ $count -ne 0 ]; then
        echo "mysql replcation is failed"
        return 1
    else
        echo "mysql replcation is sucess"
        return 0
    fi
}
main(){
    while true
    do
        CheckDb
        sleep 30
    done
}
main

测试结果如下:

bash 复制代码
[root@oldboy  scripts]#  sed  -i  's#Slave_IO_Running:  No#Slave_IO_Running:
Yes#g' slave.log  #<==模拟IO线程恢复正常。
[root@oldboy scripts]# sh 13_6_2.sh
mysql replcation is sucess
mysql replcation is sucess
mysql replcation is sucess
^C
[root@oldboy  scripts]#  sed  -i  's#Slave_IO_Running:  Yes#Slave_IO_Running:
No#g' slave.log    #<==提示复制出现问题。
[root@oldboy scripts]# sh 13_6_2.sh
mysql replcation is failed
mysql replcation is failed
^C

方法3(此为企业生产的正式检查脚本)​:

bash 复制代码
[root@oldboy scripts]# cat 13_6_3.sh
#! /bin/bash
###########################################
# this script function is :
# check_mysql_slave_replication_status
# USER          YYYY-MM-DD - ACTION
# oldboy        2009-02-16 - Created
############################################
path=/server/scripts                         #<==定义脚本存放路径,请大家注意这个规范。
MAIL_GROUP="1111@qq.com 2222@qq.com"         #<==邮件列表,以空格隔开。
PAGER_GROUP="18600338340 18911718229"        #<==手机列表,以空格隔开。
LOG_FILE="/tmp/web_check.log"                #<==日志路径。
USER=root                                    #<==数据库用户。
PASSWORD=oldboy123                           #<==用户密码。
PORT=3307 #<==端口。
MYSQLCMD="mysql -u$USER -p$PASSWORD -S /data/$PORT/mysql.sock"
                                             #<==登录数据库命令。
error=(1008 1007 1062)                       #<==可以忽略的主从复制错误号。
RETVAL=0
[ ! -d "$path" ] && mkdir -p $path
function JudgeError(){                       #<==定义判断主从复制错误的函数。
    for((i=0; i<${#error[*]}; i++))
    do
        if [ "$1" == "${error[$i]}" ]        #<==如果传入的错误号和数组里的元素相匹配,则执行then后面的命令。
          then
            echo "MySQL slave errorno is $1, auto repairing it."
            $MYSQLCMD -e "stop slave; set global sql_slave_skip_counter=1; start slave; "
                                          #<==自动修复。
        fi
    done
    return $1
}
function CheckDb(){                       #<==定义检查数据库主从复制状态的函数。
    status=($(awk -F ':' '/_Running|Last_Errno|_Behind/{print $NF}' slave.log))
    expr ${status[3]} + 1 &>/dev/null #<==这个是延迟状态值,用于进行是否为数字的判断。
    if [ $? -ne 0 ]; then                 #<==如果不为数字。
        status[3]=300                     #<==赋值300,当数据库出现复制故障时,延迟这个状态值有可能是NULL,即非数字。
    fi
    if [ "${status[0]}" == "Yes" -a "${status[1]}" == "Yes" -a ${status[3]} -lt 120 ]
    #<==两个线程都为Yes,并且延迟小于120秒,即认为复制状态是正常的。
      then
        #echo "Mysql slave status is ok"
        return 0                   #<==返回0。
    else
        #echo "mysql replcation is failed"
        JudgeError ${status[2]}    #<==否则,将错误号${status[2]}传入JudgeError函数,判断错误号是否可以自动修复。
    fi
}
function MAIL(){                   #<==定义邮件函数,在范例11-13中讲过此函数。
    local SUBJECT_CONTENT=$1       #<==将函数的第一个传参赋值给主题变量。
    for MAIL_USER   in `echo $MAIL_GROUP`  #<==遍历邮件列表。
    do
        mail -s "$SUBJECT_CONTENT " $MAIL_USER <$LOG_FILE #<==发邮件。
    done
}
function PAGER(){#<==定义手机函数,在范例11-13中讲过此函数。
    for PAGER_USER  in `echo $PAGER_GROUP` #<==遍历手机列表。
    do
        TITLE=$1                   #<==将函数的第一个传参赋值给主题变量。
        CONTACT=$PAGER_USER        #<==将手机号赋值给CONTACT变量。
        HTTPGW=http://oldboy.sms.cn/smsproxy/sendsms.action
        #<==发送短信地址,这个地址需要用户付费购买,如果想要免费的,就得用微信替代了。
        #send_message method1
        curl -d  cdkey=5ADF-EFA -d password=OLDBOY -d phone=$CONTACT -d message=
"$TITLE[$2]" $HTTPGW
        #<==发送短信报警的命令。cdkey是购买短信网关时,由售卖者提供的,password是密码,也是由售卖者提供的。
    done
}
function SendMsg(){
    if [ $1 -ne 0 ] #<==传入$1,如果不为0,则表示复制有问题,这里的$1即为CheckDb里的返回值(用检测失败的次数作为返回值),在后文执行主函数main时是通过调用SendMsg传参传进来的值。
      then
        RETVAL=1
        NOW_TIME=`date +"%Y-%m-%d %H:%M:%S"`        #<==报警时间。
        SUBJECT_CONTENT="mysql slave is error, errorno is $2, ${NOW_TIME}."
                                                    #<==报警主题。
        echo -e "$SUBJECT_CONTENT"|tee $LOG_FILE #<==输出信息,并记录到日志。
        MAIL $SUBJECT_CONTENT #<==发邮件报警,$SUBJECT_CONTENT作为函数参数传给MAIL函数体的$1。
        PAGER $SUBJECT_CONTENT $NOW_TIME #<==发短信报警,$SUBJECT_CONTENT作为函数参数传给MAIL函数体的$1, $NOW_TIME作为函数体传给$2。
    else
        echo "Mysql slave status is ok"
        RETVAL=0 #<==以0作为返回值。
    fi
    return $RETVAL
}
function main(){
    while true
    do
        CheckDb
        SendMsg $? #<==传入第一个参数"$? ",即CheckDb里的返回值(用检测失败的次数作为返回值)。
        sleep 30
    done
}
main

6、合格运维人员必会的脚本列表

作为一个合格的运维人员,需要掌握的脚本知识列表如下:

  • 1)系统及各类服务的监控脚本,例如:文件、内存、磁盘、端口,URL监控报警等。
  • 2)监控网站目录下的文件是否被篡改,以及当站点目录被批量篡改后如何批量恢复它们的脚本。
  • 3)各类服务Rsync、Nginx、MySQL等的启动及停止专业脚本(使用chkconfig管理)。
  • 4)MySQL主从复制监控报警,以及自动处理不复制故障的脚本。
  • 5)一键配置MySQL多实例、一键配置MySQL主从部署的脚本。
  • 6)监控HTTP、MySQL、Rsync、NFS、Memcached等服务是否异常的生产脚本。
  • 7)一键软件安装及优化的脚本,比如LANMP、Linux一键优化,一键数据库安装、优化等。
  • 8)MySQL多实例启动脚本,分库、分表自动备份脚本。
  • 9)根据网络连接数及Web日志PV数封IP的脚本。
  • 10)监控网站的PV及流量,并且对流量信息进行统计的脚本。
  • 11)检查Web服务器多个URL地址是否异常的脚本,要是可以批量处理且通用的脚本。
  • 12)对系统的基础配置一键优化的脚本。
  • 13)TCP连接状态及IP统计报警的脚本。
  • 14)批量创建用户并设置随机8位密码的脚本。
相关推荐
2401_891957311 小时前
简单了解多路转接select
运维·服务器
2302_1111 小时前
java依赖小结
java·运维·数据库
blueSatchel2 小时前
ros-humble仿真-建图-导航
linux·ros2
Lucis__2 小时前
基于责任链模式的消息队列—异步处理流水线的最佳实践
linux·c++·消息队列·责任链模式·ipc
程序员-Benothing2 小时前
Shell 脚本条件判断与流程控制:if for while case 详解
linux·运维·服务器
mounter6252 小时前
统一安全域:KVM Planes 如何驾驭硬件特权级
linux·安全·linux kernel·kernel
君生我老2 小时前
Linux动静态库
linux·服务器
IT摆渡者3 小时前
基于 Docker 部署 Nextcloud 私有云盘(外挂 Windows 共享)项目实践
运维·docker·容器
宵时待雨3 小时前
linux笔记归纳19:网络层协议IP
linux·网络·笔记·网络协议·tcp/ip