shell 100案例
每日磁盘情况
【题目要求】
请按照这样的日期格式(xxxx-xx-xx)每日生成一个文件,
例如生成的文件名为2017-12-20.log,
并且把磁盘的使用情况写到到这个文件中,
不用考虑cron,仅仅写脚本即可
【习题答案】
本题答案:
#! /bin/bash
d=`date +%F`
logfile=$d.log
df -h > $logfile
需求升级:
#!/bin/bash
d=`date +%F`
dir=/data/logs/disklog
if [ ! -d $dir ]
then
mkdir -p $dir
fi
df -h > $dir/$d.log
find $dir/ -mtime +365 |xargs rm
统计IP访问流量
题目要求
有日志1.log,部分内容如下
112.111.12.248 – [25/Sep/2013:16:08:31 +0800]formula-x.haotui.com “/seccode.php?update=0.5593110133088248″ 200″http://formula-x.haotui.com/registerbbs.php” “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)”
61.147.76.51 – [25/Sep/2013:16:08:31 +0800]xyzdiy.5d6d.com “/attachment.php?aid=4554&k=9ce51e2c376bc861603c7689d97c04a1&t=1334564048&fid=9&sid=zgohwYoLZq2qPW233ZIRsJiUeu22XqE8f49jY9mouRSoE71″ 301″http://xyzdiy.5d6d.com/thread-1435-1-23.html” “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)”
统计出每个IP访问量有多少
核心要点
awk、sort、uniq命令
参考答案
awk '{print $1}' 1.log|sort |uniq -c |sort -n -r
所有进程占用内存和
题目要求
写一个脚本计算一下linux系统所有进程占用内存大小的和。
核心要点
- ps命令用法
- for循环
- 加法运算
参考答案
sum=0
#过过滤掉TIME COMMAND的行
mem=$(ps aux|grep -v 'TIME COMMAND'|awk '{print $4}')
for i in $mem
do
sum=$(echo "$sum+$i"|bc) #小数计算要用bc命令
done
echo "所有进程一共占用$sum内存"
机器存活状态
题目要求
设计一个脚本,监控远程的一台机器(假设ip为180.163.26.39)的存活状态,当发现宕机时发一封邮件给你自己。
核心要点
ping -c10 180.163.26.39 通过ping来判定对方是否在线
发邮件脚本
参考答案
#ping 服务器宕机情况
ip=www.baidu.com
n=$(ping $ip -c5 |grep packets|awk -F "%" '{print $1}' |awk '{print $NF}')
if [ -z "$n" ]
then
echo "脚本有问题。"
python3 /root/py/mail.py $mail "检测机器存活脚本$0有问题" "获取变量的值为空"
exit
else
n1=$(echo $n|sed 's/[0-9]//g')
if [ -n "$n1" ]
then
echo "脚本有问题。"
python3 /root/py/mail.py $mail "检测机器存活脚本$0有问题" "获取变量的值不是纯数字"
exit
fi
fi
mail=71718132@qq.com
while :
do
if [ $n -gt 50 ];then
python3 /root/py/mail.py $mail "机器宕机" "丢包率是$n%"
else
echo "服务器没有问题"
break
fi
sleep 10
done
找文件改名
题目要求
找到/123目录下所有后缀名为.txt的文件
1. 批量修改.txt为.txt.bak
2. 把所有.bak文件打包压缩为123.tar.gz
3. 批量还原文件的名字,即把增加的.bak再删除
核心要点
- find用来查找所有.txt文件
- tar打包一堆文件
- 还原文件名用for循环
参考答案
#!/bin/bash
find /123/ -type f -name "*.txt" > /tmp/txt.list
for f in `cat /tmp/txt.list`
do
mv $f $f.bak
done
#find /123/ -type f -name *.txt |xargs -i mv {} {}.bak
#find /123/ -type f -name *.txt -exec mv {} {}.bak \;
for f in `cat /tmp/txt.list`
do
echo $f.bak
done > /tmp/txt.bak.list
tar -czvf 123.tar.gz `cat /tmp/txt.bak.list |xargs `
for f in `cat /tmp/txt.list`
do
mv $f.bak $f
done
判断80端口存活情况
题目要求
写一个脚本,判断本机的80端口(假如服务为httpd)是否开启着,如果开启着什么都不做,如果发现端口不存在,那么重启一下httpd服务,
并发邮件通知你自己。脚本写好后,可以每一分钟执行一次,也可以写一个死循环的脚本,30s检测一次。
核心要点
- 检测80端口使用nmap -p 80 127.0.0.1或者netstat -lntp|grep -w 80
- 重启httpd服务的命令要知道
- 发邮件脚本依然使用mail.py
参考答案
#!/bin/bash
m=123@123.com
while :
do
n=`netstat -lntp |grep ':80 '|wc -l`
if [ $n -eq 0 ]
then
/usr/local/apache2/bin/apachectl -k restart 2>/tmp/apache.err
python mail.py $m "80端口关闭" "已经重启httpd服务"
pn=`pgrep -l httpd|wc -l`
if [ $pn -eq 0 ]
then
python mail.py $m "httpd重启失败" "`head -1 /tmp/apache.err`"
fi
fi
sleep 30
done
备份数据库并传到远程服务器
题目要求
设计一个shell脚本来备份数据库,首先在本地服务器上保存一份数据,然后再远程拷贝一份,本地保存一周的数据,远程保存一个月。
假定,我们知道mysql root账号的密码,要备份的库为discuz,本地备份目录为/bak/mysql, 远程服务器ip为192.168.123.30,
远程提供了一个rsync服务,备份的地址是 192.168.123.30::backup . 写完脚本后,需要加入到cron中,每天凌晨3点执行。
核心要点
- 备份数据库的命令
- 同步到远程去的命令
- 本地一周,可以用date +%w做为后缀,远程一个月可以用date +%d做为后缀
参考答案
#!/bin/bash
d1=`date +%w`
d2=`date +%d`
local_bakdir=/bak/mysql
remote_bakdir=192.168.123.30::backup
exec 1> /tmp/mysqlbak.log 2>/tmp/mysqlbak.err
echo "mysql bakcup begin at `date`"
mysqldump -uroot -pxxxx discz > $local_bakdir/discuz.sql.$d1
rsync -az $local_bakdir/discuz.sql.$d1 $remote_bakdir/discuz.sql.$d2
echo "mysql backup end at `date`"
检查nginx 502状态
题目要求
服务器上跑的是LNMP环境,近期总是有502现象。502为网站访问的状态码,200正常,502错误是nginx最为普遍的错误状态码。
由于502只是暂时的,并且只要一重启php-fpm服务则502消失,但不重启的话,则会一直持续很长时间。
所以有必要写一个监控脚本,监控访问日志的状态码,一旦发生502,则自动重启一下php-fpm。
我们设定:
1)access_log /data/log/access.log
2)脚本死循环,每10s检测一次(假设每10s钟的日志条数为300左右)
3)重启php-fpm的方法是 /etc/init.d/php-fpm restart
核心要点
- 用curl检测状态码是否是502或者通过分析访问日志判断状态码的比率
- 重启php-fpm服务的命令
参考答案
#!/bin/bash
log=/data/log/access.log
while :
do
502_n=`tail -n 300 $log |grep -c ' 502 '`
if [ -z "$502_n" ]
then
exit
fi
if [ $502_n -gt 100 ]
then
/etc/init.d/php-fpm restart >/dev/null 2>/tmp/php-fpm.err
fpm_p_n=`pgrep -l php-fpm|wc -l`
if [ $fpm_p_n -eq 0 ]
then
python mail.py xxx@xx.com "php-fpm重启失败" "`head -1 /tmp/php-fpm.err`"
exit
fi
fi
sleep 10
done
sed命令包含字母行删掉
题目要求
把一个文本文档的前5行中包含字母的行删除掉,同时把6到10行中的全部字母删除掉。
核心要点
sed命令
参考答案
#!/bin/bash
sed -n '1,5'p 1.txt |sed '/[a-zA-Z]/d'
sed '1,5d' 1.txt |sed '1,5s/[a-zA-Z]//g'
找出字母数小于6的单词
题目要求
用shell打印下面这句话中字母数小于6的单词。
Bash also interprets a number of multi-character options.
核心要点
- for循环遍历所有单词
- wc -L获取字符串长度
参考答案
#!/bin/bash
c="Bash also interprets a number of multi-character options."
n=`echo $c|awk -F '[ +-.]' '{print NF}'`
for ((i=1;i<$n;i++))
do
l=`echo $c|awk -F '[ +-.]' -v j=$i '{print $j}'|wc -L`
if [ $l -lt 6 ]
then
echo $c|awk -F '[ +-.]' -v j=$i '{print $j}'
fi
done
case按数字执行对应命令
题目要求
写一个脚本实现如下功能:
输入一个数字,然后运行对应的一个命令。
显示命令如下:
cmd meau* 1 - date 2 - ls 3 - who 4 - pwd
当输入1时,会运行date, 输入2时运行ls, 以此类推。
核心要点
- case判断
参考答案
#!/bin/bash
echo "*cmd meau** 1 - date 2 - ls 3 - who 4 - pwd"
read -p "Please input a number: " n
if [ -z "$n" ]
then
echo "请输入一个纯数字,范围1-4."
exit
fi
n1=`echo $n|sed 's/[0-9]//g'`
if [ -n "$n1" ]
then
echo "请输入一个纯数字,范围1-4."
exit
fi
case $n in
1)
date
;;
2)
ls
;;
3)
who
;;
4)
pwd
;;
*)
echo "请输入1-4的数字"
;;
esac
批量创建用户产生随机密码
题目要求
用shell脚本实现如下需求:
添加user_00 – user_09 10个用户,并且给他们设置一个随机密码,密码要求10位包含大小写字母以及数字,注意需要把每个用户的密码记录到一个日志文件里。
提示:
- 随机密码使用命令 mkpasswd
- 在脚本中给用户设置密码,可以使用echo 然后管道passwd命令
核心要点
- seq实现数字递增
- mkpasswd产生随机字符
参考答案
#!/bin/bash
for i in `seq -w 00 09` #-w选项:指定输出数字同宽
do
useradd user_$i
p=`mkpasswd -l 10 -s 0 ` #产生随机密码,mkpasswd命令,yum install expect
echo "user_$i $p" >> /tmp/pass.tmp
echo $p |passwd --stdin user_$i
done
检测apache服务,发送邮件
题目要求
在服务器上,写一个监控脚本,要求如下:
- 每隔10s去检测一次服务器上的httpd进程数,如果大于等于500的时候,就需要自动重启一下apache服务,并检测启动是否成功?
- 若没有正常启动还需再一次启动,最大不成功数超过5次则需要立即发邮件通知管理员,并且以后不需要再检测!
- 如果启动成功后,1分钟后再次检测httpd进程数,若正常则重复之前操作(每隔10s检测一次),若还是大于等于500,那放弃重启并需要发邮件给管理员,然后自动退出该脚本。假设其中发邮件脚本为之前使用的mail.py
核心要点
- pgrep -l httpd或者ps -C httpd --no-heading检查进程
- for循环5次计数器
参考答案
#!/bin/bash
check_service()
{
n=0
for i in `seq 1 5`
do
/usr/local/apache2/bin/apachectl restart 2>/tmp/apache.err
if [ $? -ne 0 ]
then
n=$[$n+1]
else
break
fi
done
if [ $n -eq 5 ]
then
##下面的mail.py参考https://coding.net/u/aminglinux/p/aminglinux-book/git/blob/master/D22Z/mail.py
python mai.py "123@qq.com" "httpd service down" `cat /tmp/apache.err`
exit
fi
}
while true
do
t_n=`ps -C httpd --no-heading |wc -l`
if [ $t_n -ge 500 ]
then
/usr/local/apache2/bin/apachectl restart
if [ $? -ne 0 ]
then
check_service
fi
sleep 60
t_n=`ps -C httpd --no-heading |wc -l`
if [ $t_n -ge 500 ]
then
python mai.py "123@qq.com" "httpd service somth wrong" "the httpd process is busy."
exit
fi
fi
sleep 10
done
web访问日志访问高的IP拒绝掉
题目要求
需求: 根据web服务器上的访问日志,把一些请求量非常高的ip给拒绝掉!并且每隔半小时把不再发起请求或者请求量很小的ip给解封。
假设:
- 一分钟内请求量高于100次的IP视为不正常请求。
- 访问日志路径为/data/logs/access_log。
用第2例中的1.log作为演示日志
核心要点
- 统计ip访问次数,排序
- 如何标记每隔半小时
- iptables计数器是一个重要的判断指标
- 函数(封IP、解封IP)
参考答案
#!/bin/bash
block_ip()
{
t1=`date -d "-1 min" +%Y:%H:%M`
log=/data/logs/access_log
egrep "$t1:[0-9]+" $log > /tmp/tmp_last_min.log
awk '{print $1}' /tmp/tmp_last_min.log |sort -n |uniq -c|sort -n |awk '$1>100 {print $2}' > /tmp/bad_ip.list
n=`wc -l /tmp/bad_ip.list|awk '{print $1}'`
if [ $n -ne 0 ]
then
for ip in `cat /tmp/bad_ip.list`
do
iptables -I INPUT -s $ip -j REJECT
done
fi
}
unblock_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
for ip in `cat /tmp/good_ip.list`
do
iptables -D INPUT -s $ip -j REJECT
done
fi
iptables -Z
}
t=`date +%M`
if [ $t == "00" ] || [ $t == "30" ]
then
unblock_ip
block_ip
else
block_ip
fi
找出数字规律输出后面的数字
题目要求
请仔细查看如下几个数字的规律,并使用shell脚本输出后面的十个数字。
10 31 53 77 105 141 …….
核心要点
- 计算两个数值之间的差值
参考答案
#!/bin/bash
x=10
y=21
for i in `seq 0 15`
do
echo $x
x=$[$x+$y]
z=$[2**$i]
y=$[$y+$z]
done
检测磁盘分区和inode使用率
题目要求
写一个shell脚本,检测所有磁盘分区使用率和inode使用率并记录到以当天日期为命名的日志文件里,当发现某个分区容量或者inode使用量大于85%时,发邮件通知你自己。
参考答案
#!/bin/bash
dir=/tmp/disk
d=`date +%F`
mail=123@123.com
[ -d $dir ] || mkdir $dir
df >> $dir/$d.log
df -i >> $dir/$d.log
df|sed '1d' |awk -F ' +|%' '$5>=85 {print $7}' > $dir/df.tmp
df -i|sed '1d' |awk -F ' +|%' '$5>=85 {print $7}' > $dir/df_i.tmp
n1=`wc -l $dir/df.tmp|awk '{print $1}'`
n2=`wc -l $dir/df_i.tmp|awk '{print $1}'`
tag=0
if [ $n1 -gt 0 ]
then
if [ $n2 -gt 0 ]
then
tag=11
else
tag=10
fi
else
if [ $n2 -gt 0 ]
then
tag=01
else
tag=00
fi
fi
case $tag in
11)
python mail.py $mail "磁盘空间和inode使用率高于85%" "`cat $dir/df.tmp $dir/df_i.tmp|xargs`"
;;
10)
python mail.py $mail "磁盘空间使用率高于85%" "`cat $dir/df.tmp|xargs`"
;;
01)
python mail.py $mail "磁盘inode使用率高于85%" "`cat $dir/df_i.tmp|xargs`"
;;
*)
;;
esac
检测目录有无上传新文件
题目要求
有一台服务器作为web应用,有一个目录(/data/web/attachment)不定时地会被用户上传新的文件,但是不知道什么时候会上传。所以,需要我们每5分钟做一次检测是否有新文件生成。
请写一个shell脚本去完成检测。检测完成后若是有新文件,还需要将新文件的列表输出到一个按年、月、日、时、分为名字的日志里。
参考答案
#!/bin/bash
basedir=/data/web/attachment
t=`date +%Y%m%d%H%M`
find $basedir/ -type f -mmin -5 > /tmp/file.list
n=`wc -l /tmp/file.list|awk '{print $1}'`
if [ $n -lt 0 ]
then
mv /tmp/file.list /tmp/$t.list
fi
常用命令TOP10
题目要求
写一个shell脚本来看看你使用最多的命令是哪些,列出你最常用的命令top10。
参考答案
cat ~/.bash_history |sort |uniq -c |sort -nr |head
histoy |awk '{print $2}'|sort |uniq -c |sort -nr |head
curl网站是否正常
题目要求
写一个shell脚本,通过curl -I 返回的状态码来判定所访问的网站是否正常。
比如,当状态码为200时,才算正常。
参考答案
#!/bin/bash
#这个脚本用来判断一个网址是否正常
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-11-01
url="http://www.apelearn.com/index.php"
mail_user=306798658@qq.com
code=`curl -I $url 2>/tmp/curl.err|head -1|awk '{print $2}'`
if [ -z "$code" ]
then
python mail.py $mail_user "$url访问异常" "`cat /tmp/curl.err`"
exit
elif [ $code != "200" ]
then
curl -I $url &> /tmp/curl.log
python mail.py $mail_user "$url访问异常 状态码$code" "`/tmp/curl.log`"
fi
打包小于5kb的文件
题目要求
将用户家目录(考虑到执行脚本的用户可能是普通用户也可能是root)下面小于5KB的文件打包成tar.gz的压缩包,并以当前日期为文件名前缀,例如,2018-03-15.tar.gz。
参考答案
#!/bin/bash
#这个脚本用来打包用户家目录下小于5k的文件
t=`date +%F`
cd $HOME
tar czf $t.tar.gz `find ./ -type f -size -5k|xargs`
统计10点到12点来访IP最多
题目要求
已知nginx访问的日志文件在/usr/local/nginx/logs/access.log内,请统计下早上10点到12点 来访ip最多的是哪个?
参考日志
111.199.186.68 -- [15/Sep/2017:09:58:37 +0800] "//plugin.php?id=security:job" 200 "POST //plugin.php?id=security:job HTTP/1.1""http://a.lishiming.net/forum.php?mod=viewthread&tid=11338&extra=page%3D1%26filter%3Dauthor%26orderby%3Ddateline" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3141.7 Safari/537.36"
203.208.60.208 -- [15/Sep/2017:09:58:46 +0800] "/misc.php?mod=patch&action=ipnotice&_r=0.05560809863330207&inajax=1&ajaxtarget=ip_notice" 200 "GET /misc.php?mod=patch&action=ipnotice&_r=0.05560809863330207&inajax=1&ajaxtarget=ip_notice HTTP/1.1""http://a.lishiming.net/forum.php?mod=forumdisplay&fid=65&filter=author&orderby=dateline" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3141.7 Safari/537.36"
参考答案
#!/bin/bash
#这个脚本用来分析Nginx访问日志
export LANG=en
log="/usr/local/nginx/logs/access.log"
t=`date +%d/%b/%Y:1[01]:[0-5][0-9]:`
egrep "$t" $log|awk '{print $1}' |sort -n |uniq -c |sort -n |tail -1 |awk '{print $2}'
题目要求
将文件内所有的单词的重复次数计算出来,只需要列出重复次数最多的10个单词。
参考答案
#!/bin/bash
#这个脚本用来找出重复的单词
for w in `sed 's/[^a-zA-Z]/ /g' 1.txt`
do
echo $w
done |sort |uniq -c |sort -nr|head
把a文件有的b文件没有的行找出来
题目要求
有两个文件a.txt和b.txt,需求是,把a.txt中有的但b.txt中没有的行找出来,并写入到c.txt,然后计算c.txt文件的行数。
参考答案
#!/bin/bash
#这个脚本用来比较文件差异
cat a.txt|while read line
do
if ! grep -q "$line" b.txt
then
echo $line
fi
done >c.txt
wc -l c.txt
进程名字中含有"aming"的进程关闭
题目要求
把当前用户下所有进程名字中含有"aming"的进程关闭。
参考答案
#!/bin/bash
#这个脚本用来杀进程
ps -u $USER|awk '$NF ~ /aming/ {print $1}' |xargs kill
监控服务器CPU使用率
题目要求
用shell写一个监控服务器cpu使用率的监控脚本。
参考答案
#!/bin/bash
#这个脚本用来计算CPU使用率
while :
do
cpu_i=`top -bn1 |grep 'Cpu(s):'|sed 's/^%//'|awk -F ' +|%' '{print $8}'`
cpu_u=`echo 100-$cpu_i|bc`
if [ $cpu_u -gt 90 ]
then
m_mail cpu #发送邮件 函数没写
fi
sleep 60
done
自动登录服务器杀掉tomcat 进程
题目要求
linux系统中,目录/root/下有一个文件ip-pwd.ini,内容如下:
10.111.11.1,root,xyxyxy
10.111.11.2,root,xzxzxz
10.111.11.3,root,123456
10.111.11.4,root,xxxxxx
......
文件中每一行的格式都为linux服务器的ip,root用户名,root密码,请用一个shell批量将这些服务器中的所有tomcat进程kill掉。
参考答案
#!/bin/bash
#这个脚本用来批量杀tomcat进程
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-12
cat > kill_tomcat.expect <<EOF
#!/usr/bin/expect
set passwd [lindex \$argv 0]
set host [lindex \$argv 1]
spawn ssh root@\$host
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "\$passwd\r" }
}
expect "]*"
send "killall java\r"
expect "]*"
send "exit\r"
EOF
chmod a+x kill_tomcat.expect
cat ip-pwd.ini|while read line
do
ip=`echo $line |awk -F ',' '{print $1}'`
pw=`echo $line |awk -F ',' '{print $3}'`
./kill_tomcat.expect $pw $ip
done
等额本金和等额本息
题目要求
贷款有两种还款的方式:等额本金法和等额本息法,简单说明一下等额本息法与等额本金法的主要区别:
等额本息法的特点是:每月的还款额相同,在月供中"本金与利息"的分配比例中,前半段时期所还的利息比例大、本金比例小,还款期限过半后逐步转为本金比例大、利息比例小。所支出的总利息比等额本金法多,而且贷款期限越长,利息相差越大。
等额本金法的特点是:每月的还款额不同,它是将贷款额按还款的总月数均分(等额本金),再加上上期剩余本金的月利息,形成一个月还款额,所以等额本金法第一个月的还款额最多 ,而后逐月减少,越还越少。所支出的总利息比等额本息法少。
两种还款方式的比较不是我们今天的讨论范围,我们的任务就是做一个贷款计算器。其中:
等额本息每月还款额的计算公式是:
每月还款额=[贷款本金×月利率×(1+月利率)^还款月数]÷[(1+月利率)^还款月数-1]
等额本金每月还款额的计算公式是:
每月还款额=贷款本金÷贷款期数+(本金-已归还本金累计额)×月利率
参考答案
#!/bin/bash
#这个脚本用来实现简易的房贷计算器
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-12
read -p "请输入贷款总额(单位:万元):" sum_w
read -p "请输入贷款年利率(如年利率为6.5%,直接输入6.5):" y_r
read -p "请输入贷款年限(单位:年):" y_n
echo "贷款计算方式:"
echo "1)等额本金计算法"
echo "2)等额本息计算法"
read -p "请选择贷款方式(1|2)" type
#贷款总额
sum=`echo "scale=2;$sum_w*10000 " | bc -l`
#年利率
y_r2=`echo "scale=6;$y_r/100 " | bc -l`
#月利率
m_r=`echo "scale=6;$y_r2/12 " | bc -l`
#期数
count=$[$y_n*12]
echo "期次 本月还款额 本月利息 未还款额"
jin()
{
#月还款本金m_jin=贷款总额sum/期数count
m_jin=`echo "scale=2;($sum/$count)/1 " | bc -l`
#定义未还本金r_jin(第一期应该是贷款总额)
r_jin=$sum
for((i=1;i<=$count;i++))
do
#本月利息m_xi=剩余本金*月利率
m_xi=`echo "scale=2;( $r_jin*$m_r)/1"|bc -l`
#本月还款m_jinxi=月还本金m_jin+本月利息m_xi
m_jinxi=`echo "scale=2;( $m_jin+$m_xi)/1"|bc -l`
#已还款本金jin=月还本金m_jin*期数i
jin=`echo "scale=2;( $m_jin*$i)/1"|bc -l`
#剩余本金r_jin=贷款总额sum-已还本金jin
r_jin=`echo "scale=2;( $sum-$jin)/1"|bc -l`
if [ $i -eq $count ]
then
#最后一月的还款额应该是每月还款本金+本月利息+剩余本金
m_jinxi=`echo "scale=2;( $m_jin+$r_jin+$m_xi)/1"|bc -l`
#最后一月的剩余本金应该是0
r_jin=0
fi
echo "$i $m_jinxi $m_xi $r_jin"
done
}
xi()
{
#每期还款m_jinxi=(贷款总额sum*月利率m_r*((1+月利率m_r)^期数count))/(((1+月利率m_r)^期数count)-1)
m_jinxi=`echo "scale=2;(($sum*$m_r*((1+$m_r)^$count))/(((1+$m_r)^$count)-1))/1 " | bc -l`
#定义未还本金r_jin(第一期应该是贷款总额)
r_jin=$sum
for((i=1;i<=$count;i++))
do
#本期利息m_xi=剩余本金r_jin*月利率m_r
m_xi=`echo "scale=2;( $r_jin*$m_r)/1"|bc -l`
#本期本金m_jin=本期本息m_jinxi-本期利息m_xi
m_jin=`echo "scale=2;($m_jinxi-$m_xi)/1 " | bc -l`
#未还本金r_jin=上期未还本金r_jin-本期应还本金m_jin
r_jin=`echo "scale=2;($r_jin-$m_jin)/1 " | bc -l`
if [ $i -eq $count ]
then
#最后一月本息m_jinxi=本期本金m_jin+未还本金r_jin
m_jinxi=`echo "scale=2;($m_jin+$r_jin)/1 " | bc -l`
#最后一月的剩余本金应该是0
r_jin="0.00"
fi
echo "$i $m_jinxi $m_xi $r_jin"
done
}
case $type in
1)
jin
;;
2)
xi
;;
*)
exit 1
;;
esac
检测磁盘io
题目要求
阿里云的机器,今天收到客服来的电话,说服务器的磁盘io很重。于是登录到服务器查看,并没有发现问题,所以怀疑是间歇性地。正要考虑写个脚本的时候,幸运的抓到了一个线索,造成磁盘io很高的幕后黑手是mysql。此时去show processlist,但未发现有问题的队列。原来只是一瞬间。只好继续来写脚本,思路是,每5s检测一次磁盘io,当发现问题去查询mysql的processlist。
参考答案
#!/bin/bash
#这个脚本用来监控磁盘IO
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-12
if ! while iostat &>/dev/null
then
yum install -y sysstat
fi
while :
do
t=`date +%T`
iostat -xd 1 5 |grep '^sda'> /tmp/io.log
sum=`awk '{sum=sum+$NF} END {print sum}' /tmp/io.log`
a=`echo "scale=2;$sum/5"|bc`
b=`echo $a|cut -d . -f 1`
if [ $b -gt 90 ]
then
mysql -uroot -pxxxx -e "show processlist" > mysql_$t.log
fi
sleep 1
done
A服务器文件自动同步到B,C服务器
题目要求
需求背景是:
一个业务,有3台服务器(A,B,C)做负载均衡,由于规模太小目前并未使用专业的自动化运维工具。有新的需求时,开发同事改完代码会把变更上传到其中一台服务器A上。但是其他2台服务器也需要做相同变更。
写一个shell脚本,把A服务器上的变更代码同步到B和C上。
其中,你需要考虑到不需要同步的目录(假如有tmp、upload、logs、caches)
参考答案
#!/bin/bash
#这个脚本用来代码上线
dir=/data/wwwroot/www.aaa.com
B_IP=1.1.1.1
C_IP=2.2.2.2
rs()
{
rsync -azP --exclude="logs"
--exclude="tmp" --exclude="upload"
--exclude="caches" $dir/ $1:$dir/
}
read -p "该脚本将会把本机的$dir下的文件同步到$B_IP和$C_IP上,是否要继续?y|n" c
case $c in
y|Y)
rs B_IP
rs C_IP
;;
n|N)
exit
;;
*)
echo "你只能输入y或者n."
;;
esac
tomcat自带脚本无法关掉,shell脚本关掉
题目要求
在生产环境中,经常遇到tomcat无法彻底关闭,也就是说用tomcat自带shutdown.sh脚本无法将java进程完全关掉。所以,需要借助shell脚本,将进程杀死,然后再启动。
写一个shell脚本,实现上述功能。彻底杀死一个进程的命令是 kill -9 pid。
参考答案
#!/bin/bash
#这个脚本用来彻底杀死Tomcat进程
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-14
dir=/usr/local/tomcat/bin/
java_pc()
{
pgrep java|wc -l
}
cd $dir
./shutdown.sh
count=0
while [ $count -lt 5 ]
do
n=`java_pc`
if [ $n -gt 0 ]
then
killall java
count=$[$count+1]
sleep 1
else
break
fi
done
n=`java_pc`
if [ $n -gt 0 ]
then
killall -9 java
fi
n=`java_pc`
if [ $n -gt 0 ]
then
echo "Tomcat无法强制杀死。"
exit
fi
cd $dir
./startup.sh
查询指定域名的过期时间
题目要求
写一个shell脚本,查询指定域名的过期时间,并在到期前一周,每天发一封提醒邮件。
参考答案
#!/bin/bash
#这个脚本用来检查域名是否到期
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-14
mail_u=admin@admin.com
#当前日期时间戳,用于和域名的到期时间做比较
t1=`date +%s`
#检测whois命令是否存在,不存在则安装jwhois包
is_install_whois()
{
which whois >/dev/null 2>/dev/null
if [ $? -ne 0 ]
then
yum install -y epel-release
yum install -y jwhois
fi
}
notify()
{
#e_d=`whois $1|grep 'Expiry Date'|awk '{print $4}'|cut -d 'T' -f 1`
e_d=`whois $1|grep 'Expiration'|tail -1 |awk '{print $5}' |awk -F 'T' '{print $1}'`
#如果e_d的值为空,则过滤关键词'Expiration Time'
if [ -z "$e_d" ]
then
e_d=`whois $1|grep 'Expiration Time'|awk '{print $3}'`
fi
#将域名过期的日期转化为时间戳
e_t=`date -d "$e_d" +%s`
#计算一周一共有多少秒
n=`echo "86400*7"|bc`
e_t1=$[$e_t-$n]
e_t2=$[$e_t+$n]
if [ $t1 -ge $e_t1 ] && [ $t1 -lt $e_t ]
then
python mail.py $mail_u "Domain $1 will to be expired." "Domain $1 expire date is $e_d."
fi
if [ $t1 -ge $e_t ] && [ $t1 -lt $e_t2 ]
then
python mail.py $mail_u "Domain $1 has been expired" "Domain $1 expire date is $e_d."
fi
}
#检测上次运行的whois查询进程是否存在
#若存在,需要杀死进程,以免影响本次脚本执行
if pgrep whois &>/dev/null
then
killall -9 whois
fi
is_install_whois
for d in aaa.com bbb.com aaa.cn
do
notify $d
done
本机公钥到对方机器上
题目要求
写一个shell脚本,当我们执行时,提示要输入对方的ip和root密码,然后可以自动把本机的公钥增加到对方机器上,从而实现密钥认证。
参考答案
#!/bin/bash
#这个脚本用来自动配置密钥认证
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-14
read -p "输入一个IP地址: " ip
read -p "输入此机器的root密码: " pasd
is_install()
{
if ! rpm -q $1 &>/dev/null
then
yum installl -y $1
fi
}
is_install openssh-clients
is_install expect
if [ ! -f ~/.ssh/id_rsa.pub ]
then
echo -e "\n" |ssh-keygen -P ''
fi
cat > key.expect <<EOF
#!/usr/bin/expect
set host [lindex \$argv 0]
set passwd [lindex \$argv 1]
spawn ssh-copy-id root@\$host
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "\$passwd\r" }
}
expect eof
EOF
chmod a+x key.expect
./key.expect $ip $pasd
部署mysql主从
题目要求
用shell脚本实现,部署mysql主从,假设两台机器上已经安装了mysql,并且目前无新库。
参考答案
#!/bin/bash
#这个脚本用来配置MySQL主从同步
#!/bin/bash
master_ip=192.168.100.12
slave_ip=192.168.100.13
mysqlc="mysql -uroot -paminglinux"
check_ok()
{
if [ $? -ne 0 ]
then
echo "$1 出错了。"
exit 1
fi
}
f_exist()
{
d=`date +%F%T`
if [ -f $1 ]
then
mv $1 $1_$d
fi
}
## 设置主mysql配置
if ! grep '^server-id' /etc/my.cnf
then
sed -i '/^\[mysqld\]$/a\server-id = 1001' /etc/my.cnf
fi
if ! grep '^log-bin.*=.*' /etc/my.cnf
then
sed -i '/^\[mysqld\]$/a\log-bin = aminglinux' /etc/my.cnf
fi
sed -i '/^log-bin.*/a\binlog-ignore-db = mysql ' /etc/my.cnf
/etc/init.d/mysqld restart
check_ok "主上重启mysql"
## 登录mysql,授权用户、锁表以及show master status。
$mysqlc <<EOF
grant replication slave on *.* to 'repl'@$slave_ip identified by 'yourpassword';
flush tables with read lock;
EOF
$mysqlc -e "show master status" > /tmp/master.log
file=`tail -1 /tmp/master.log|awk '{print $1}'`
pos=`tail -1 /tmp/master.log|awk '{print $2}'`
## 创建在从上配置和操作的脚本
f_exist /tmp/slave.sh
cat > /tmp/slave.sh << EOF
#!/bin/bash
if ! grep '^server-id' /etc/my.cnf
then
sed -i '/^\[mysqld\]$/a\server-id = 1002' /etc/my.cnf
fi
/etc/init.d/mysqld restart
check_ok "从上重启mysql"
$mysqlc <<EOF
stop slave;
change master to master_host="$master_ip", master_user="repl", master_password="yourpassword", master_log_file="$file", master_log_pos=$pos;
start slave;
EOF
EOF
## 创建传输slave.sh的expect脚本
f_exist /tmp/rs_slave.expect
cat > /tmp/rs_slave.expect <<EOF
#!/usr/bin/expect
set passwd "aminglinux"
spawn rsync -a /tmp/slave.sh root@$slave_ip:/tmp/slave.sh
expect {
"yes/no" { send "yes\r"}
"password:" { send "\$passwd\r" }
}
expect eof
EOF
## 执行expect脚本
chmod +x /tmp/rs_slave.expect
/tmp/rs_slave.expect
check_ok "传输slave.sh"
## 创建远程执行命令的expect脚本
f_exist /tmp/exe.expect
cat > /tmp/exe.expect <<EOF
#!/usr/bin/expect
set passwd "aminglinux"
spawn ssh root@$slave_ip
expect {
"yes/no" { send "yes\r"}
"password:" { send "\$passwd\r" }
}
expect "]*"
send "/bin/bash /tmp/slave.sh\r"
expect "]*"
send "exit\r"
EOF
## 执行expect脚本
chmod +x /tmp/exe.expect
/tmp/exe.expect
check_ok "远程执行slave.sh"
## 主上解锁表
$mysqlc -e "unlock tables"
管理docker容器,启动/关闭/删除容器
题目要求
写一个脚本,实现一键管理docker容器,比如启动、关闭、删除容器等操作。
要求:
1. 脚本支持启动全部容器、关闭全部容器、删除全部容器
2. 需要提示用户如何使用该脚本,需给出范例
参考答案
#!/bin/bash
#这个脚本用来管理docker容器
while true
do
read -p "请输入你要执行的操作:(stop/start/rm) " opt
if [ -z "$opt" ]
then
echo "请输入要执行的操作。"
continue
else
break
fi
done
docker ps -a |awk '{print $1}' > /tmp/id.txt
case $opt in
stop)
for id in `cat /tmp/id.txt`
do
docker stop $id
done
;;
start)
for id in `cat /tmp/id.txt`
do
docker start $id
done
rm)
for id in `cat /tmp/id.txt`
do
read -p "将要删除容器$id,是否继续?(y|n)" c
case $c in
y|Y)
docker rm -f $id
;;
n|N)
echo "容器$id不会被删除。"
;;
*)
echo "你只能输入'y'或者'n'。"
;;
esac
done
*)
echo "你只能输入start/stop/rm。"
;;
esac
一键安装配置samba服务,共享目录
题目要求
写个shell脚本,能够实现一键安装并配置samba服务,执行该脚本时需要带一个参数,为共享的目录,目录若不存在,需自动创建。
要求,任何人都可以访问,且不用密码,并且目录是只读的。
参考答案
#!/bin/bash
#这个脚本用来一键安装并配置samba
if [ "$#" -ne 1 ]
then
echo "运行脚本的格式为:$0 /dir/"
exit 1
else
if ! echo $1 |grep -q '^/.*'
then
echo "请提供一个绝对路径。"
exit 1
fi
fi
if ! rpm -q samba >/dev/null
then
echo "将要安装samba"
sleep 1
yum install -y samba
if [ $? -ne 0 ]
then
echo "samba安装失败"
exit 1
fi
fi
cnfdir="/etc/samba/smb.conf"
cat >> $cnfdir <<EOF
[share]
comment = share all
path = $1
browseable = yes
public = yes
writable = no
EOF
if [ ! -d $1 ]
then
mkdir -p $1
fi
chmod 777 $1
echo "test" > $1/test.txt
#假设系统为CentOS7
systemctl start smb
if [ $? -ne 0 ]
then
echo "samba服务启动失败,请检查配置文件是否正确。"
else
echo "samba配置完毕,请验证。"
fi
新的磁盘自动格式化,搭载
题目要求
我们使用的云主机,购买一块云盘后,默认并不是挂载状态的,用shell写一个脚本,只要把盘符和挂载点以参数的形式提供给脚本,该脚本就可以自动格式化、挂载。
要求:
1. 不用分区,直接格式化
2. 格式化为ext4文件系统类型
参考答案
#!/bin/bash
#这个脚本用来自动挂载磁盘
if [ $# -ne 2 ]
then
echo "Useage $0 盘符 挂载点, 如: $0 /dev/xvdb /data"
exit 1
fi
if [ ! -b $1 ]
then
echo "你提供的盘符不正确,请检查后再操作"
exit 1
fi
echo "格式化$1"
mkfs -t ext4 $1
if [ ! -d $2 ] ;then
mkdir -p $2
fi
n=`awk '$NF == "$2"' /etc/fstab|wc -l`
if [ $n -eq 0 ]
then
echo "$1 $2 ext4 defaults 0 0" >> /etc/fstab
mount -a
else
echo "配置文件/etc/fstab中已经存在挂载点$2,请检查一下."
exit 1
fi
备份数据库里的100个库
题目要求
需求背景:
领导要求小明备份数据库服务器里面的100个库(数据量在几十到几百G),需要以最快的时间完成(5小时内),并且不能影响服务器性能。
参考答案
#!/bin/bash
#这个脚本用来并发备份数据库
##假设100个库的库名、host、port以及配置文件路径存到了一个文件里,文件名字为/tmp/databases.list
##格式:db1 10.10.10.2 3308 /data/mysql/db1/my.cnf
##备份数据库使用xtrabackup(由于涉及到myisam,命令为inoobackupex)
exec &> /tmp/mysql_bak.log
if ! which innobackupex &>/dev/nll
then
echo "安装xtrabackup工具"
rpm -ivh http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm && \
yum install -y percona-xtrabackup-24
if [ $? -ne 0 ]
then
echo "安装xtrabackup工具出错,请检查。"
exit 1
fi
fi
bakdir=/data/backup/mysql
bakuser=vyNctM
bakpass=99omeaBHh
function bak_data {
db_name=$1
db_host=$2
db_port=$3
cnf=$4
[ -d $bakdir/$db_name ] || mkdir -p $bakdir/$db_name
innobackupex --defaults-file=$4 --host=$2 --port=$3 --user=$bakuser --password=$bakpass $bakdir/$1
if [ $? -ne 0 ]
then
echo "备份数据库$1出现问题。"
fi
}
fifofile=/tmp/$$
mkfifo $fifofile
exec 1000<>$fifofile
thread=10
for ((i=0;i<$thread;i++))
do
echo >&1000
done
cat /tmp/databases.list | while read line
do
read -u1000
{
bak_data `echo $line`
echo >&1000
} &
done
wait
exec 1000>&-
rm -f $fifofile
截取字符
题目要求
利用你学过的知识点,想办法根据要求截取出字符。
字符串var=http://www.aaa.com/root/123.htm
1.取出www.aaa.com/root/123.htm
2.取出123.htm
4.取出http:
5.取出http://
6.取出root/123.htm
7.取出123
参考答案
#!/bin/bash
#这个脚本用来截取字符串
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-19
var=http://www.aaa.com/root/123.htm
echo "1.取出www.aaa.com/root/123.htm"
echo $var |awk -F '//' '{print $2}'
echo "2.取出123.htm"
echo $var |awk -F '/' '{print $5}'
echo "3.取出http://www.aaa.com/root"
echo $var |sed 's#/123.htm##'
echo "4.取出http:"
echo $var |awk -F '//' '{print $1}'
echo "5.取出http://"
echo $var |awk -F 'www' '{print $1}'
echo "6.取出root/123.htm"
echo $var |awk -F 'com/' '{print $2}'
echo $var |awk -F '/' '{print $4"/"$5}'
echo "7.取出123"
echo $var |sed 's/[^0-9]//g'
字符串合并
题目要求
请把下面的字符串:
zhangsan
y97JbzPru
lisi
5JhvCls6q
xiaowang
Nnr8qt2Ma
laoma
iqMtvC02y
zhaosi
9fxrb4sJD
改为如下:
zhangsan:y97JbzPru
lisi:5JhvCls6q
xiaowang:Nnr8qt2Ma
laoma:iqMtvC02y
zhaosi:9fxrb4sJD
参考答案
#!/bin/bash
#这个脚本用来格式化文本
n=`wc -l test3.txt|awk '{print $1}'`
n2=$[$n/2]
for i in `seq 1 $n2`
do
i2=$[$i*2]
j=$[$i2-1]
l1=`sed -n "$i2"p test3.txt`
l2=`sed -n "$j"p test3.txt`
echo $l2:$l1
done
修改rm命令,并做备份
题目要求
linux系统的rm命令太危险,一不小心就会删除掉系统文件。 写一个shell脚本来替换系统的rm命令,要求当删除一个文件或者目录时,都要做一个备份,然后再删除。下面分两种情况,做练习:
1. 简单
假设有一个大的分区/data/,每次删除文件或者目录之前,都要先在/data/下面创建一个隐藏目录,以日期/时间命名,比如/data/.201703271012/,然后把所有删除的文件同步到该目录下面,可以使用rsync -R 把文件路径一起同步
2. 复杂
不知道哪个分区有剩余空间,在删除之前先计算要删除的文件或者目录大小,然后对比系统的磁盘空间,如果够则按照上面的规则创建隐藏目录,并备份,如果没有足够空间,要提醒用户没有足够
的空间备份并提示是否放弃备份,如果用户选择y,则直接删除文件或者目录,如果选择n,则提示未删除,然后退出脚本。
参考答案
1\. 简单
#!/bin/bash
#这个脚本用来自定义rm
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-19
filename=$1
big_filesystem=/data/
if [ ! -e $1 ]
then
echo "$1 不存在,请使用绝对路径"
exit
fi
d=`date +%Y%m%d%H%M`
read -p "Are U sure delete the file or directory $1? y|n: " c
case $c in
y|Y)
mkdir -p $big_filesystem/.$d && rsync -aR $1 $big_filesystem/.$d/$1 && /bin/rm -rf $1
;;
n|N)
exit 0
;;
*)
echo "Please input 'y' or 'n'."
;;
esac
2.复杂
#!/bin/bash
#这个脚本用来自定义rm
#!/bin/bash
filename=$1
if [ ! -e $1 ]
then
echo "$1 不存在,请使用绝对路径"
exit
fi
d=`date +%Y%m%d%H%M`
f_size=`du -sk $1|awk '{print $1}'`
disk_size=`LANG=en; df -k |grep -vi filesystem|awk '{print $4}' |sort -n |tail -n1`
big_filesystem=`LANG=en; df -k |grep -vi filesystem |sort -n -k4 |tail -n1 |awk '{print $NF}'`
if [ $f_size -lt $disk_size ]
then
read -p "Are U sure delete the file or directory: $1? y|n: " c
case $c in
y|Y)
mkdir -p $big_filesystem/.$d && rsync -aR $1 $big_filesystem/.$d/$1 && /bin/rm -rf $1
;;
n|N)
exit 0
;;
*)
echo "Please input 'y' or 'n'."
;;
esac
else
echo "The disk size is not enough to backup the files $1."
read -p "Do you want to delete $1? y|n: " c
case $c in
y|Y)
echo "It will delete $1 after 5 seconds whitout backup."
for i in `seq 1 5`; do echo -ne ". "; sleep 1;done
echo
/bin/rm -rf $1
;;
n|N)
echo "It will not delete $1."
exit 0
;;
*)
echo "Please input 'y' or 'n'."
;;
esac
fi