shell是什么
shell是一种脚本语言 aming_linux blog.lishiming.net
可以使用逻辑判断、循环等语法
可以自定义函数
shell是系统命令的集合
shell脚本可以实现自动化运维能大大增加我们的运维效率
shell脚本结构和执行方法
开头需要加#!/bin/bash固有的格式
意思就是接下来的文件是由/bin/bash解析的
以#开头的行作为解释说明
脚本的名字以.sh结尾用于区分这是一个shell脚本
执行方法有两种
先给这个文件加一个执行权限在./例如1.sh文件chmod +x 1.sh; ./1.sh
bash 1.sh
查看脚本执行过程bash -x 1.sh
查看脚本是否语法错误 bash -n 1.sh 如果没有任何输出证明他没有错误
date命令使用方法
data显示当前系统的日期和时间
data +%Y表示四位的年
data +%y两位的年
data +%M分钟
data +%m月份
data +%d日期
data +%D日/月/年
data +%Y%m%d年0月0日
data +%F年-月-日
data +%H小时
data +%S分钟
data +%H:%M:%S 小时分钟秒=data +%T
data +%h英文月份
data +%w周几
data +%W今年的第几周
cal显示日历
data +%s时间戳
时间戳换算日期date -d @1504620492
查看以前的日期-d
date -d "+1day" 一天后
date -d "-1 day" 一天前
date -d "-1 month" 一月前
date -d "-1 min" 一分钟前
date +%w, date +%W 星期
shell脚本中的变量
当脚本中使用某个字符串较频繁并且字符串长度很长时就应该使用变量代替
使用条件语句时常使用变量 if [ $a -gt 1 ]; then ... ; fi
引用某个命令的结果时用变量替代 n=`wc -l 1.txt`
写和用户交互的脚本时变量也是必不可少的 read -p "Input a number: " n; echo $n 如果没写这个n可以直接使用$REPLY
内置变量 $0, $1, $2… $0表示脚本本身$1 第一个参数$2 第二个 .... $#表示参数个数
数学运算a=1;b=2; c=$(($a+$b))或者$[$a+$b]
shell中的逻辑判断
格式1if 条件 ; then 语句; fi
-gt大于 -lt小于
例如a=5如果a>5echo就ok
a=5
if [ $a -gt 3 ]; then echo ok; fi
格式2if 条件; then 语句; else 语句; fi
例如a=5如果a>3echo就ok如果不大于3就不ok
格式3if …; then … ;elif …; then …; else …; fi
逻辑判断表达式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
或者if [ $b -gt 5 ] || [ $b -lt 3 ]; then
if判断文件、目录属性
[ -f file ]判断是否是普通文件且存在
[ -d file ] 判断是否是目录且存在
[ -e file ] 判断文件或目录是否存在
[ -r file ] 判断文件是否可读
[ -w file ] 判断文件是否可写
[ -x file ] 判断文件是否可执行
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脚本案例
#!/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" asldkfls@11.com
fi
sleep 30
done
案例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
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
continue结束本次循环
忽略continue之下的代码直接进行下一次循环
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i == 3 ]
then
continue
fi
echo $i
done
echo $i
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
}
函数必须要放在最前面
示例1 (打印一个参数
#!/bin/bash
input() {
echo $1 $2 $# $0
}
input 1 a b
示例2
#!/bin/bash
sum() {
s=$[$1+$2]
echo $s
}
sum 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"
shell中的数组1
定义数组 a=(1 2 3 4 5); echo ${a[@]}
echo ${#a[@]} 获取数组的元素个数
echo ${a[2]} 读取第三个元素数组从0开始
echo ${a[*]} 等同于 ${a[@]} 显示整个数组
数组赋值
a[1]=100; echo ${a[@]}
a[5]=2; echo ${a[@]} 如果下标不存在则会自动添加一个元素
数组的删除
uset a; unset a[1]
2
数组分片
a=(`seq 1 5`)
echo ${a[@]:0:3} 从第一个元素开始截取3个
echo ${a[@]:1:4} 从第二个元素开始截取4个
echo ${a[@]:0-3:2} 从倒数第3个元素开始截取2个
数组替换
echo ${a[@]/3/100}
a=(${a[@]/3/100})
shell项目-告警系统
需求使用shell定制各种个性化告警工具但需要统一化管理、规范化管理。
思路指定一个脚本包包含主程序、子程序、配置文件、邮件引擎、输出日志等。
主程序作为整个脚本的入口是整个系统的命脉。
配置文件是一个控制中心用它来开关各个子程序指定各个相关联的日志文件。
子程序这个才是真正的监控脚本用来监控各个指标。
邮件引擎是由一个python程序来实现它可以定义发邮件的服务器、发邮件人以及发件人密码
输出日志整个监控系统要有日志输出。
程序架构 主目录 mon ____________________|_______________________________ | | | | | bin conf shares mail log | | | | | [main.sh] [ mon.conf] [load.sh 502.sh] [mail.py mail.sh] [ mon.log err.log ]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 aming_test@163.com "$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 $user@192.168.133.132
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 root@192.168.133.132:/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脚本批量把需要同步的文件分发到目标机器即可。
核心命令rsync -av --files-from=list.txt / root@host:/
shell项目-分发系统-构建文件分发系统
文件分发系统的实现
rsync.expect 内容
#!/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内容
192.168.133.132
192.168.133.133
......
shell项目-分发系统-构建文件分发系统
rsync.sh 内容
#!/bin/bash
for ip in `cat ip.list`
do
echo $ip
./rsync.expect $ip list.txt
done
shell项目-分发系统-命令批量执行
exe.expect 内容
#!/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项目-分发系统-命令批量执行
exe.sh 内容
#!/bin/bash
for ip in `cat ip.list`
do
echo $ip
./exe.expect $ip "w;free -m;ls /tmp"
done