Notes 16 (shell programming)

What is shell

shell is a scripting language aming_linux blog.lishiming.net

 Analyzing logic may be used, the syntax circulation

 You can customize function

 shell is a collection of system commands

 shell scripts to automate the operation and maintenance can greatly increase the efficiency of our operation and maintenance


shell script structure and execution methods

 Beginning need to add #! / Bin / bash inherent format

It means that the next file is / bin / bash resolution

 # Row beginning with an explanation as

 Name of the script ends in .sh used to distinguish which is a shell script

There are two ways to perform:

(1) Give the file plus an executive authority 'x' 

For example: chmod a + x 1.sh so that it can be performed at ./1.sh relative path.

(2)bash 1.sh

(3)sh 1.sh

View script execution bash -x 1.sh

 See if script syntax errors   bash -n 1.sh when a script syntax error he would have a hint, if he does not prove there is no output error.

image.png


Use the date command

date and time data in the current system

Data +% the Y represent four of 

data +% y of two years

Data +% M min 

data +% m Month

date data +% d 

the Data + D% month / day / year

wKioL1m84XqTDS__AABBRzDIEwA104.png

data +% Y% m% d day 0 in October

wKiom1m84b3yvPtLAABMPg0U_VU274.png

the Data + F% year - month - day

wKioL1m84aKCM_yfAABFNDdX0BI383.png

Data +% H

Data +% S seconds

data +% H:% M:% S h min = data +% T seconds

wKioL1m84c-QobUNAACKcywwSLM404.png

data +% h English month

data +% w of week

the Data +% W week number of year

cal show calendar

timestamp data +% s

Timestamp conversion date DATE -d @ 1,504,620,492

wKiom1m84lOABU-JAADWREHBMCM142.png

Conversion date stamp date +% s -d "2019-2-14 22:35:49"

image.png

View previous date -d

 date -d "+ 1day" the day after

 date -d "-1 day" the day before

 date -d "-1 month" in January before

 date -d "-1 min" a minute ago

 date +% w, date +% W week

wKiom1m84tHQO-BCAAIZgqFe3uQ293.png


shell script variables

When a script using a string and should be used more frequently when the length of the string variable instead of a long

 Using conditional statements often use variables if [$ a -gt 1]; then ...; fi

 Alternatively when n with reference to the result of a command variable = `wc -l 1.txt`

 Write scripts and user interaction when a variable is also essential to read -p "Input a number:" n; echo $ n n If you did not write this can directly use $ REPLY

 Built-in variables $ 0, $ 1, $ 2 ... $ 0 for the script itself, $ 1 the first parameter $ 2 a second .... $ # is the number of parameters

 Math a = 1; b = 2; c = $ (($ a + $ b)) or $ [$ a + $ b] 


Analyzing logic of the shell

 Format. 1: IF conditions; then statement; Fi

-gt greater than  -lt less than

也可以用大于小于号,但是必须加双括号:(($a>3));(($a<3))

举例:

a=5

如果$a大于3

则输出ok

fi结尾

wKiom1m852TDm2VDAAA3-yq4dWY580.png

格式2:if 条件; then 语句; else 语句; fi

举例:

a=5

如果a>3

则输出就ok

否则输出nook

wKiom1m86XviC3i1AABY0620Oz4608.png

格式3:if …; then … ;elif …; then …; else …; fi

举例:

a=4

如果$a大于4

则输出a大

在如果$a小于4

则输出大于1&&小于4

否则输出无效

QQ Browser screenshot 20190218214409.png

逻辑判断表达式if [ $a -gt $b ]; if [ $a -lt 5 ]; if [ $b -eq 10 ]等于

-gt:大于 (>); 

-lt:小于(<); 

-ge:大于等于(>=); 

-le:小于等于(<=);

-eq:等于(==); 

-ne:不等于(!=) 注意到处都是空格

可以使用 &&(并且);||(或者) 结合多个条件

if [ $a -gt 5 ] && [ $a -lt 10 ]; then

解释:如果$a大于5并且$a小于10,则输出...

if [ $b -gt 5 ] || [ $b -lt 3 ]; then

解释:如果$b大于5或者$b小于3,则输出...


if判断文件、目录属性

 [ -f file ]判断是否是普通文件且存在

 [ -d file ] 判断是否是目录且存在

 wKioL1m-NIXhmcrUAAB-oHx-Jkk190.png

 [ -e file ] 判断文件或目录是否存在

 [ -r file ] 判断文件是否可读

 [ -w file ] 判断文件是否可写

 [ -x file ] 判断文件是否可执行

f="/usr/xjw"

[ -f $f ] && rm-rf $f

等同于

if [ -f $f ]

then

rm -rf $f


[ -f $f ] || touch $f

等同于

if [ ! -f $f ]

then

touch $f


shell中的case判断


if判断的一些特殊用法

 if [ -z "$a" ]  这个表示当变量a的值为空时会怎么样

 if [ -n "$a" ] 表示当变量a的值不为空

 if grep -q '123' 1.txt; then  表示如果1.txt中含有'123'的行时会怎么样

 if [ ! -e file ]; then 表示文件不存在时会怎么样

 if (($a<1)); then …等同于 if [ $a -lt 1 ]; then… 

 [ ] 中不能使用<,>,==,!=,>=,<=这样的符号


shell中的case判断

格式:

case 变量名 in

 value1)

command

;;

value2)

command

;;

*)除此自外

commond

;;

esac

在case程序中,可以在条件中使用|,表示或的意思, 比如

2|3)

command

;;

shell脚本案例

#!/bin/bash

read -p "Please input a number: " n

if [ -z "$n" ]

then

    echo "Please input a number."

    exit 1

fi


n1=`echo $n|sed 's/[0-9]//g'`

if [ -n "$n1" ]

then

 echo "Please input a number."

 exit 1

fi


if [ $n -lt 60 ] && [ $n -ge 0 ]

then

    tag=1

elif [ $n -ge 60 ] && [ $n -lt 80 ]

then

    tag=2

elif [ $n -ge 80 ]  && [ $n -lt 90 ]

then

    tag=3

elif [ $n -ge 90 ] && [ $n -le 100 ]

then

    tag=4

else 

    tag=0

fi


for循环

语法格式for 变量名 in 条件; do …; done

 案例1求1-100的和

 #!/bin/bash

sum=0

for i in `seq 1 100`

do

    sum=$[$sum+$i]

    echo $i

done

echo $sum


for循环案例2

文件列表循环把etc下每个文件ls列一下

 #!/bin/bash

cd /etc/

for a in `ls /etc/`

do

    if [ -d $a ]

    then

       ls -d $a

    fi

done


while循环

语法格式while 条件; do … ; done

 案例1

 #!/bin/bash

while :

do

    load=`w|head -1|awk -F 'load average: ' '{print $2}'|cut -d. -f1`

    if [ $load -gt 10 ]

    then

        top|mail -s "load is high: $load" [email protected]

    fi

    sleep 30

done

注:(while:死循环的意思)跟while true一样


案例2

#!/bin/bash

while :

do

    read -p "Please input a number: " n

    if [ -z "$n" ]

    then

        echo "you need input sth."

        continue

    fi

    n1=`echo $n|sed 's/[0-9]//g'`

    if [ -n "$n1" ]

    then

        echo "you just only input numbers."

        continue

    fi

    break

done

echo $n

continue:结束本次循环,如果没达到目的会直接忽略continue下边的代码,直接进行下一次循环

continue是跳出当前条件循环,继续下一轮条件循环

break:跳出来,退出这个循环

break是立马跳出循环;continue是跳出当前条件循环,继续下一轮条件循环;

exit是直接退出整个脚本

如果写脚本的时候输入汉字发现是乱码,那证明系统变量语言不对LANG

用echo $LANG或者env|grep LANG看一下变量值如果是LANG=en就把他改成LANG=zh_CN.UTF-8


break跳出循环

#!/bin/bash

for i in `seq 1 5`

do

    echo $i//输出i的值

    if [ $i == 3 ]//当i等于三的时候跳循环就是跳出for循环

    then

        break

    fi

    echo $i

done

echo aaaaaaa

wKiom1m_nnGxwhvSAAB6G6Ix-kE067.png


continue结束本次循环

忽略continue之下的代码直接进行下一次循环

#!/bin/bash

for i in `seq 1 5`

do

    echo $i

    if [ $i == 3 ]

    then

        continue

    fi

    echo $i

done

echo $i

wKioL1m_nlTBm5N-AABy2FIaCWc229.png


exit直接退出脚本

#!/bin/bash

for i in `seq 1 5`

do

    echo $i

    if [ $i == 3 ]

    then

        exit

    fi

    echo $i

done

echo aaaaaaa


shell脚本中的函数

函数就是把一段代码整理到了一个小单元中并给这个小单元起一个名字当用到这段代码时直接调用这个小单元的名字即可。

格式: function f_name() {                      

       command             

     }

解释:function后边跟函数的名字,或者可以不写function,直接写函数的名字:函数名()

函数必须要放在最前面

 示例1 (打印一个参数

#!/bin/bash

input() {

    echo $1 $2 $# $0

}


input 1 a b


示例2 

#!/bin/bash

sum() {

    s=$[$1+$2]

    echo $s

}

sum 1 2

解释:首先定义一个函数sum,函数里边定义一个变量s,变量的内容是第一个参数与第二个参数相加,然后输出函数的结果,最下边sum是用来引用它,sum 1 2意思就是说1+2


示例3 输入网卡的名字显示网卡的IP0

#!/bin/bash

ip() {

    ifconfig |grep -A1 "$1 " |tail -1 |awk '{print $2}'|awk -F':' '{print $2}'

}

read -p "Please input the eth name: " e

myip=`ip $e`

echo "$e address is $myip"


image.png

事例4:

#!/bin/bash

read -p "please inpute the ent name:" n

ip()

ifconfig |grep -A1 "$1: " | awk '/inet/ {print $2}'

}

ip $n


shell中的数组1

定义数组 

格式:

a=(1 2 3 4 5); echo ${a[@]}

[root@localhost ~]# a=(1 2 3 4 5); echo ${a[@]}
1 2 3 4 5

a=(1 2 3 4 5); echo ${a[*]}

[root@localhost ~]# a=(1 2 3 4 5); echo ${a[*]}
1 2 3 4 5

查看指定某一元素的值:更改[ ]里边的值就可以

数组特性从零开始,0代表第一个,1代表第二个,依次往后排列

[root@localhost ~]# a=(1 2 3 4 5); echo ${a[0]}
1
[root@localhost ~]# a=(1 2 3 4 5); echo ${a[1]}
2
[root@localhost ~]# a=(1 2 3 4 5); echo ${a[2]}
3

echo ${#a[@]} 获取数组的元素个数 

[root@localhost ~]# echo ${#a[@]}
5

 

数组赋值

 a[1]=100; echo ${a[@]}如果下标或者说赋值的元素存在那么就会被替换或者说覆盖掉之前的

[root@localhost ~]# echo ${a[@]} //数组内容为1 2 3 4 5 a
1 2 3 4 5 a
[root@localhost ~]# a[1]=100     //从新给元素赋值100
[root@localhost ~]# echo ${a[@]} //因为存在就会被替换掉,变成1 100 3 4 5 a
1 100 3 4 5 a
[root@localhost ~]#

 a[5]=a; echo ${a[@]} 如果下标不存在则会自动添加一个元素

[root@localhost ~]# a=(1 2 3 4 5) //a赋值为1 2 3 4 5
[root@localhost ~]# echo ${a[@]}  //打印a数组
1 2 3 4 5
[root@localhost ~]# a[5]=a        //给第五个元素赋值为a
[root@localhost ~]# echo ${a[@]}  //因为第五个元素不存在所以会增加一个
1 2 3 4 5 a


 数组的删除

unset a 删除整个数组,其实跟取消赋值类似

[root@localhost ~]# echo ${a[@]}
1 100 4 5 a
[root@localhost ~]# unset a
[root@localhost ~]# echo ${a[@]}

[root@localhost ~]#

unset a[2] 删除指定的下标元素

[root@localhost ~]# echo ${a[@]}
1 100 3 4 5 a
[root@localhost ~]# unset a[2]
[root@localhost ~]# echo ${a[@]}
1 100 4 5 a


数组分片

以 分割,第一个冒号后边表示你要从第几个元素开始截取,第二个冒号后边表示你要截取几个

 a=(`seq 1 10`)

 echo ${a[@]:0:3} 从第三个元素开始截取4个

[root@localhost ~]# a=(`seq 1 10`)
[root@localhost ~]# echo ${a[@]}
1 2 3 4 5 6 7 8 9 10
[root@localhost ~]# echo ${a[@]:3:4}
4 5 6 7

 echo ${a[@]:1:4} 从第二个元素开始截取4个

[root@localhost ~]# echo ${a[@]}
1 2 3 4 5 6 7 8 9 10
[root@localhost ~]# echo ${a[@]:1:4}
2 3 4 5

 echo ${a[@]:0-3:2} 从倒数第3个元素开始截取2个

[root@localhost ~]# echo ${a[@]}
1 2 3 4 5 6 7 8 9 10
[root@localhost ~]# echo ${a[@]:0-3:2}
8 9

echo ${a[@]:0-4:4} 从倒数第4个元素开始截取4个

[root@localhost ~]# echo ${a[@]}
1 2 3 4 5 6 7 8 9 10
[root@localhost ~]# echo ${a[@]:0-4:4}
7 8 9 10


数组替换

用/分割,第一个/后边表示被替换的元素,第二个/后边表示要替换的元素

比如把数组中的3替换成100

 echo ${a[@]/3/100}输出时替换

[root@localhost ~]# echo ${a[@]}
1 2 3 4 5 6 7 8 9 10
[root@localhost ~]# echo ${a[@]/3/100}
1 2 100 4 5 6 7 8 9 10

 a=(${a[@]/3/100})永久替换,其实就是赋值

[root@localhost ~]# a=(${a[@]/3/100})
[root@localhost ~]# echo ${a[@]}
1 2 100 4 5 6 7 8 9 10


shell项目-告警系统

需求使用shell定制各种个性化告警工具但需要统一化管理、规范化管理。

思路指定一个脚本包包含主程序、子程序、配置文件、邮件引擎、输出日志等。

主程序作为整个脚本的入口是整个系统的命脉。

配置文件是一个控制中心用它来开关各个子程序指定各个相关联的日志文件。

子程序这个才是真正的监控脚本用来监控各个指标。

邮件引擎是由一个python程序来实现它可以定义发邮件的服务器、发邮件人以及发件人密码

输出日志整个监控系统要有日志输出。

要求:我们的机器角色多种多样但是所有机器上都要部署同样的监控系统也就说所有机器不管什么角色整个程序框架都是一致的不同的地方在于根据不同的角色定制不同的配置文件。

image.png

bin下是主程序

conf下是配置文件

shares下是各个监控脚本

mail下是邮件引擎

log下是日志。



shell项目-告警系统main.sh 

main.sh内容

 #!/bin/bash

#Written by aming.

# 是否发送邮件的开关

export send=1

# 过滤ip地址

export addr=`/sbin/ifconfig |grep -A1 "ens33: "|awk '/inet/ {print $2}'`

dir=`pwd`

# 只需要最后一级目录名

last_dir=`echo $dir|awk -F'/' '{print $NF}'`

# 下面的判断目的是保证执行脚本的时候我们在bin目录里不然监控脚本、邮件和日志很有可能找不到

if [ $last_dir == "bin" ] || [ $last_dir == "bin/" ]; then

    conf_file="../conf/mon.conf"

else

    echo "you shoud cd bin dir"

    exit

fi

exec 1>>../log/mon.log 2>>../log/err.log

echo "`date +"%F %T"` load average"

/bin/bash ../shares/load.sh

#先检查配置文件中是否需要监控502

if grep -q 'to_mon_502=1' $conf_file; then

    export log=`grep 'logfile=' $conf_file |awk -F '=' '{print $2}' |sed 's/ //g'`

    /bin/bash  ../shares/502.sh

fi


shell项目-告警系统mon.conf 

 mon.conf内容

 ## to config the options if to monitor

## 定义mysql的服务器地址、端口以及user、password

to_mon_cdb=0   ##0 or 1, default 0,0 not monitor, 1 monitor

db_ip=10.20.3.13

db_port=3315

db_user=username

db_pass=passwd

## httpd   如果是1则监控为0不监控

to_mon_httpd=0

## php 如果是1则监控为0不监控

to_mon_php_socket=0

## http_code_502  需要定义访问日志的路径

to_mon_502=1

logfile=/data/log/xxx.xxx.com/access.log

## request_count   定义日志路径以及域名

to_mon_request_count=0

req_log=/data/log/www.discuz.net/access.log

domainname=www.discuz.net


shell项目-告警系统load.sh

load.sh内容

 #! /bin/bash

##Writen by aming##

load=`uptime |awk -F 'average:' '{print $2}'|cut -d',' -f1|sed 's/ //g' |cut -d. -f1`

if [ $load -gt 10 ] && [ $send -eq "1" ]

then

    echo "$addr `date +%T` load is $load" >../log/load.tmp

    /bin/bash ../mail/mail.sh [email protected] "$addr\_load:$load" `cat ../log/load.tmp`

fi

echo "`date +%T` load is $load"


shell项目-告警系统502.sh

 502.sh内容

#! /bin/bash

d=`date -d "-1 min" +%H:%M`

c_502=`grep :$d:  $log  |grep ' 502 '|wc -l`

if [ $c_502 -gt 10 ] && [ $send == 1 ]; then

     echo "$addr $d 502 count is $c_502">../log/502.tmp

     /bin/bash ../mail/mail.sh $addr\_502 $c_502  ../log/502.tmp

fi

echo "`date +%T` 502 $c_502"


shell项目-告警系统disk.sh

disk.sh内容

#! /bin/bash

##Writen by aming##

rm -f ../log/disk.tmp

for r in `df -h |awk -F '[ %]+' '{print $5}'|grep -v Use`

do

    if [ $r -gt 90 ] && [ $send -eq "1" ]

then

    echo "$addr `date +%T` disk useage is $r" >>../log/disk.tmp

fi

if [ -f ../log/disk.tmp ]

then

    df -h >> ../log/disk.tmp

    /bin/bash ../mail/mail.sh $addr\_disk $r ../log/disk.tmp

    echo "`date +%T` disk useage is nook"

else

    echo "`date +%T` disk useage is ok"

fi

注释:'[ %]+'以空格或者百分号为分隔符,+表示一个或者多个,结合起来就是以一个或多个空格或者百分号为分隔符(类似:'[:#]+'以冒号或者警号)

shell项目-告警系统mail.sh

 mail.sh内容 //其中mail.py内容到这里下载https://coding.net/u/aminglinux/p/aminglinux-book/git/blob/master/D22Z/mail.py

  log=$1

t_s=`date +%s`

t_s2=`date -d "2 hours ago" +%s`

if [ ! -f /tmp/$log ]

then

    echo $t_s2 > /tmp/$log

fi

t_s2=`tail -1 /tmp/$log|awk '{print $1}'`

echo $t_s>>/tmp/$log

v=$[$t_s-$t_s2]

echo $v

if [ $v -gt 3600 ]

then

    ./mail.py  $1  $2  $3

    echo "0" > /tmp/$log.txt

else

    if [ ! -f /tmp/$log.txt ]

    then

        echo "0" > /tmp/$log.txt

    fi

    nu=`cat /tmp/$log.txt`

    nu2=$[$nu+1]

    echo $nu2>/tmp/$log.txt

    if [ $nu2 -gt 10 ]

    then

         ./mail.py  $1 "trouble continue 10 min $2" "$3"

         echo "0" > /tmp/$log.txt

    fi

fi  


shell项目-分发系统-expect讲解

yum install -y expect

  自动远程登录

 #! /usr/bin/expect

set host "192.168.133.132"

set passwd "123456"

spawn ssh root@$host

expect {

"yes/no" { send "yes\r"; exp_continue}

"assword:" { send "$passwd\r" }

}

interact


shell项目-分发系统-expect讲解

自动远程登录后执行命令并退出

#!/usr/bin/expect

set user "root"

set passwd "123456"

spawn ssh [email protected]


expect {

"yes/no" { send "yes\r"; exp_continue}

"password:" { send "$passwd\r" }

}

expect "]*"

send "touch /tmp/12.txt\r"

expect "]*"

send "echo 1212 > /tmp/12.txt\r"

expect "]*"

send "exit\r"


shell项目-分发系统-expect讲解

  传递参数

#!/usr/bin/expect


set user [lindex $argv 0]

set host [lindex $argv 1]

set passwd "123456"

set cm [lindex $argv 2]

spawn ssh $user@$host


expect {

"yes/no" { send "yes\r"}

"password:" { send "$passwd\r" }

}

expect "]*"

send "$cm\r"

expect "]*"

send "exit\r"


shell项目-分发系统-expect讲解

自动同步文件 

#!/usr/bin/expect

set passwd "123456"

spawn rsync -av [email protected]:/tmp/12.txt /tmp/

expect {

"yes/no" { send "yes\r"}

"password:" { send "$passwd\r" }

}

expect eof


shell项目-分发系统-expect讲解

指定host和要同步的文件

#!/usr/bin/expect

set passwd "123456"

set host [lindex $argv 0]

set file [lindex $argv 1]

spawn rsync -av $file root@$host:$file

expect {

"yes/no" { send "yes\r"}

"password:" { send "$passwd\r" }

}

expect eof


shell项目-分发系统-构建文件分发系统

需求背景对于大公司而言肯定时不时会有网站或者配置文件更新而且使用的机器肯定也是好多台少则几台多则几十甚至上百台。所以自动同步文件是至关重要的。

 实现思路首先要有一台模板机器把要分发的文件准备好然后只要使用expect脚本批量把需要同步的文件分发到目标机器即可。

 The core command rsync -av --files-from = list.txt / root @ host: /


shell project - distribution system - build file distribution system

 Implementation file distribution system

 rsync.expect content

#!/usr/bin/expect

set passwd "123456"

set host [lindex $argv 0]

set file [lindex $argv 1]

spawn rsync -av --files-from=$file / root@$host:/

expect {

"yes/no" { send "yes\r"}

"password:" { send "$passwd\r" }

}

expect eof

 ip.list content

192.168.133.132

192.168.133.133

......


shell project - distribution system - build file distribution system

 rsync.sh content

#!/bin/bash

for ip in `cat ip.list`

do

    echo $ip

    ./rsync.expect $ip list.txt

done


shell project - distribution system - the command batch execution

 exe.expect content

#!/usr/bin/expect

set host [lindex $argv 0]

set passwd "123456"

set cm [lindex $argv 1]

spawn ssh root@$host

expect {

"yes/no" { send "yes\r"}

"password:" { send "$passwd\r" }

}

expect "]*"

send "$cm\r"

expect "]*"

send "exit\r"


shell project - distribution system - the command batch execution

exe.sh content

#!/bin/bash

for ip in `cat ip.list`

do

    echo $ip

    ./exe.expect $ip "w;free -m;ls /tmp"

done


Guess you like

Origin blog.51cto.com/12922638/2423459