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.
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
data +% Y% m% d day 0 in October
the Data + F% year - month - day
Data +% H h
Data +% S seconds
data +% H:% M:% S h min = data +% T seconds
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
Conversion date stamp date +% s -d "2019-2-14 22:35:49"
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
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结尾
格式2:if 条件; then 语句; else 语句; fi
举例:
a=5
如果a>3
则输出就ok
否则输出nook
格式3:if …; then … ;elif …; then …; else …; fi
举例:
a=4
如果$a大于4
则输出a大
在如果$a小于4
则输出大于1&&小于4
否则输出无效
逻辑判断表达式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 ] 判断是否是目录且存在
[ -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
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
}
解释: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"
事例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程序来实现它可以定义发邮件的服务器、发邮件人以及发件人密码
输出日志整个监控系统要有日志输出。
要求:我们的机器角色多种多样但是所有机器上都要部署同样的监控系统也就说所有机器不管什么角色整个程序框架都是一致的不同的地方在于根据不同的角色定制不同的配置文件。
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