来自 阿铭linux视频号
练习30 打印方块
写一个shell脚本,交互式,根据提示,需要用户输入一个数字作为参考,最终打印出一个正方形。
正方形的组成使用特殊字符■,可以直接打印出来。
示例,如果用户输入数字为5,则最终显示的效果为
■ ■ ■ ■ ■
■ ■ ■ ■ ■
■ ■ ■ ■ ■
■ ■ ■ ■ ■
■ ■ ■ ■ ■
cat >30.sh<<'EOF'
#!/bin/bash
#auth:alibaby007
#version:v1
#date:2024-08-08
read -p "please input a number:" sum
a=`echo $sum | sed 's/[0-9]//g'`
if [ -n "$a" ]
then
echo "请输入一个纯数字"
exit 1
fi
for n in `seq $sum`
do
for m in `seq $sum`
do
if [ $m -lt $sum ]
then
echo -n "■ "
else
echo "■"
fi
done
done
EOF
练习31 打印三角
用户输入一个数字,然后打印一个三角形,比如用户输入5,打印如下图形:
★
★ ★
★ ★ ★
★ ★ ★ ★
★ ★ ★ ★ ★
cat >31.sh<<'EOF'
#!/bin/bash
#auth:alibaby007
#version:v1
#date:2024-08-08
#死循环,如果用户输入的不是纯数字,那就重新输入
while true
do
read -p "please input the lenth: " n
#判断有没有输入字符
if [ -z $n ]
then
echo "要输入一个数字"
continue
else
#用户输入了字符,要判断输入的字符是不是纯数字
n1=`echo $n | sed 's/[0-9]//g'`
if [ -n "$n1" ]
then
echo "你输入的不是纯数字,重新输入"
#如果输入的不是纯数字,再次循环
continue
else
#如果是纯数字,退出循环
break
fi
fi
done
#i为行,j为列
for i in `seq 1 $n`
do
#先打印空格,第一行空格为n-1,第二行为n-2,一直到0
#这里用n-i来表示
j=$[$n-$i]
for m in `seq $j`
do
#用echo -n为了不换行
echo -n " "
done
#打印完空格,开始打印 ★ + 空格
#第一行打印1个,第二行打印2个,一直到n个
for p in `seq 1 $i`
do
echo -n "★ "
done
#因前面不换行,所以最后要换行,这里直接echo即可
echo
done
EOF
练习32 监控主机存活
写一个shell脚本,监控远程的一台机器(假设IP为192.168.77.181)的存活状态,当发现宕机时发一封邮件给自己
核心命令
ping -c10 192.168.77.181
发邮件脚本
cat >mail2.py<<'EOF'
#!/usr/bin/python
#coding:utf-8
import smtplib
from email.mime.text import MIMEText
import sys
mail_host = 'smtp.163.com'
mail_user = 'abcdefg@xx.com'
mail_pass = '1111111'
mail_postfix = '163.com'
def send_mail(to_list,subject,content):
me = "zabbix 监控告警平台"+"<"+mail_user+"@"+mail_postfix+">"
msg = MIMEText(content, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = me
msg['to'] = to_list
try:
s = smtplib.SMTP()
s.connect(mail_host)
s.login(mail_user,mail_pass)
s.sendmail(me,to_list,msg.as_string())
s.close()
return True
except Exception,e:
print str(e)
return False
if __name__ == "__main__":
send_mail(sys.argv[1], sys.argv[2], sys.argv[3])
EOF
cat >32.sh<<'EOF'
#!/bin/bash
#auth:alibaby007
#version:v1
#date:2024-08-08
IP="192.168.77.181"
email="934352862@qq.com"
n=`ping -c5 $IP| grep 'packet'| awk -F '%' '{print $1}' | awk '{print $NF}'`
if [ -z "$n" ]
then
echo "脚本有问题"
exit 1
else
n1=`echo $n| sed 's/[0-9]//g'`
if [ -n "$n1" ]
then
echo "脚本有问题"
exit 1
fi
fi
if [ $n -ge 20 ]
then
echo "机器$IP宕机,丢包率是${n}%"
#phthon mail.py $email "机器$IP宕机,丢包率是${n}%"
else
echo "机器$IP正常,丢包率是${n}%"
fi
EOF
练习33 备份nysql数据库脚本
写一个shell脚本,用来备份数据库,首先在本地服务器上保存一份数据,然后再远程拷贝一份,本地保存一周的数据,远程保存一个月。
假定,我们知道Mysql root账号的密码,要备份的库为dz,本地备份目录为/bak/mysql,
远程服务器ip为192.168.123.30,远程提供了一个rsync服务,备份地址是192.168.123.30::backup
写完脚本后,需要加入cron中,每天凌晨3点执行,cron这部分不用考虑
cat >33.sh<<'EOF'
#!/bin/bash
#auth:alibaby007
#version:v1
#date:2024-08-08
#当脚本执行到某一步有问题,立即退出脚本,后续部分不再执行了
set -e
#%w本周的第几天,新产生的会覆盖旧的,正好保留一周;
d1=`date +%w`
#%d本月的第几天,新的会覆盖旧的,正好保留一月
d2=`date +%d`
local_bakdir=/bak/mysql
remote_bakdir=192.168.123.30::backup
bak()
{
echo "mysql backup begin at `date`"
echo "执行mysqldump,备份文件为$local_bakdir/dz.sql.$d1"
mysqldump -uroot -p123456 dz >$local_bakdir/dz.sql.$d1
echo "远程拷贝到$remote_bakdir/dz.sql.$d2"
rsync -az $local_bakdir/dz.sql.$d1 $local_bakdir/dz.sql.$d2
echo "mysql backup end at `date`"
}
#bak函数执行过程的日志,输出到日志文件
bak >>${local_bakdir}/mysqlbak.log 2>>${local_bakdir}/mysqlbak.err
#关闭set -e功能
set +e
<<'COMMENT'
set -e命令用于设置shell的退出行为,脚本执行出错,立即退出,而不是继续执行后续的命令
在条件语句(如if或while)中执行的,或者是在后台运行的(使用&),或者是在管道(|)中,那么即使命令失败,set -e也不会导致脚本退出
COMMENT
EOF
练习34 wc -l 行数 wc -L 长度 shell中的变量$i赋给awk中的j
写一个脚本,打印出这句话中字母数小于6的单词
Bash also interprets a number of multi-character options.
cat >34.sh<<'EOF'
#!/bin/bash
#auth:alibaby007
#version:v1
#date:2024-08-08
#将这句话赋值给变量c
c="Bash also interprets a number of multi-character options."
#以空白字符或-或.作为分割符,看一共有多少段
#获取单词的个数,+的意思空格可以有多个
n=`echo $c|awk -F '[ +-.]' '{print NF}'`
#这里要注意,最后一段为空,并非单词,需要排除掉
for i in `seq $[$n-1]`
do
#遍历所有单词
w=`echo $c|awk -F '[ +-.]' -v j=$i '{print $j}'`
#获取单词的长度
l=`echo $w|wc -L`
if [ $l -lt 6 ]
then
echo "单词 $w 长度小于6"
fi
done
<<'COMMENT'
awk -v j=$i shell中的变量$i赋给awk中的j
wc -l 行数 wc -L 长度
COMMENT
EOF
练习35 检测服务状态
写一个shell监控脚本,要求如下
1)每隔10s去检测一次服务器上的httpd进程数,如果大于等于500的时候,就需要自动重启一下apache服务,并检测启动是否成功
2)若没有正常启动还需再一次启动,最大不成功数超过5次则需要立即发邮件通知管理员,并且以后不需要再检测
3)如果启动成功后,1分钟后再次检测httpd进程数,若正常则重复之前操作(每隔10s检测一次),若还是大于等于500,
那放弃重启并需要发邮件给管理员,然后自动退出该脚本。
4)其中发邮件脚本为mail.py
发邮件脚本
cat >mail2.py<<'EOF'
#!/usr/bin/python
#coding:utf-8
import smtplib
from email.mime.text import MIMEText
import sys
mail_host = 'smtp.163.com'
mail_user = 'abcdefg@xx.com'
mail_pass = '1111111'
mail_postfix = '163.com'
def send_mail(to_list,subject,content):
me = "zabbix 监控告警平台"+"<"+mail_user+"@"+mail_postfix+">"
msg = MIMEText(content, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = me
msg['to'] = to_list
try:
s = smtplib.SMTP()
s.connect(mail_host)
s.login(mail_user,mail_pass)
s.sendmail(me,to_list,msg.as_string())
s.close()
return True
except Exception,e:
print str(e)
return False
if __name__ == "__main__":
send_mail(sys.argv[1], sys.argv[2], sys.argv[3])
EOF
cat >35.sh<<'EOF'
#!/bin/bash
#auth:alibaby007
#version:v1
#date:2024-08-08
#定义重启并检测apache服务的函数
check_service()
{
#n为一个计数器,初始值为0
n=0
#尝试重启apache 3次
for i in `seq 1 3`
do
/usr/sbin/apachectl restart restart 2>/tmp/apache.err
if [ $? -ne 0 ]
then
##如果apache启动不成功,计数器加1
n=$[$n+1]
sleep 5
else
#如果apache启动成功,直接退出for循环
break
fi
done
#3次都没有成功,就要发邮件了
if [ $n -eq 3 ]
then
python mail.py "123@qq.com" "httpd service down" `cat /tmp/apache.err`
exit 0
fi
}
#监控脚本为一个死循环,每隔10s检测一次
while true
do
#计算https进程数量
p_n=`ps -C httpd --no-heading | wc -l`
#如果进程数大于等于500
if [ ${p_n} -ge 500 ]
then
#重启apache
/usr/sbin/apachectl restart restart
#如果重启失败,需要运行check_service函数,该函数会尝试重启3次,3次都失败则发邮件,并退出脚本
if [ $? -ne 0 ]
then
check_service
fi
#休眠60秒,继续检测httpd进程数
sleep 60
p_n=`ps -C httpd --no-heading | wc -l`
#如果进程数还是大于等于500,则发邮件告警
if [ ${p_n} -ge 500 ]
then
python mail.py "123@qq.com" "httpd service down" `cat /tmp/apache.err`
exit 0
fi
fi
sleep 10
done
EOF
练习36 自动封IP
写一个shell脚本,需求:
根据web服务器上的访问日志,把一些请求量非常高的ip给拒绝掉
并且每隔半小时把不再发起请求或者请求量很小的ip给解封
假设:一分钟内请求量高于100次的IP视为不正常请求。访问日志路径为/data/logs/access_log
日志示例
192.168.77.1 - - [11/Aug/2024:17:28:24 +0800] "GET / HTTP/1.1" 403 3539 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko
) Chrome/125.0.0.0 Safari/537.36"
cat >36.sh<<'EOF'
#!/bin/bash
#auth:alibaby007
#version:v1
#date:2024-08-08
#定义封IP的函数
block_ip()
{
#上一分钟
t1=`date -d "-1 min" +%Y:%H:%M`
log=/data/logs/access_log
#将上一分钟的日志截取出来定向输入到/tmp/tmp_last_min.log
egrep "$t1:[0-9]+" $log >/tmp/tmp_last_min.log
#把IP访问次数超过100次的计算出来,写入到临时文件
awk '{print $1}' /tmp/tmp_last_min.log | sort -n | uniq -c | awk '$1>100 {print $2}' >/tmp/bad_ip.list
#看临时文件的行数
n=`wc -l /tmp/bad_ip.list | awk '{print $1}'`
#如果临时文件行数为0,说明前面没有过滤出IP,否则就是过滤出来了
if [ $n -ne 0 ]
then
#遍历所有满足条件的IP,然后封掉这些IP
for ip in `cat /tmp/bad_ip.list`
do
iptables -I INPUT -s $ip -j REJECT
done
fi
#删除临时文件
rm -f tmp/tmp_last_min.log /tmp/bad_ip.list
}
#定义解封IP的函数
unlock_ip()
{
#将包数少于5个的IP计入IP白名单临时文件里
iptables -nvL INPUT | sed '1d' | awk '$1<5 {print $8}' >/tmp/good_ip.list
#计算白名单临时文件行数
n=`wc -l /tmp/good_ip.list | awk '{print $1}'`
#如果文件不为空
if [ $n -ne 0 ]
then
#遍历所有IP,一次解封
for ip in `cat /tmp/good_ip.list`
do
iptables -D INPUT -s $ip -j REJECT
done
fi
#最后需要将iptables统计的流量计数器清空,从零开始
iptables -Z
#删除临时文件
rm -f /tmp/good_ip.list
}
#获取当前时间中的分钟
t=`date +%M`
#如果分红为0或者30,也就是说每隔半小时会执行封IP的函数
#先解封,再封
if [ $t == "00" ] || [ $t == "30" ]
then
unblock_ip
block_ip
else
block_ip
fi
EOF