shell编程案例

1 每日一文件

【题目要求】

  1. 请按照这样的日期格式(xxxx-xx-xx)每日生成一个文件, 例如生成的文件名为2017-12-20.log, 并且把磁盘的使用情况写到到这个文件中, 不用考虑cron,仅仅写脚本即可。
  2. 并将生成的文件放入指定目录中,比如/data/logs/disklog/
  3. 找到1年以前的文件并删除掉
    【解析】
    1,首先获取到当天的日期,把日期作为变量进行赋值,
    2,然后定义一下目录的变量,检查是否存在,如果不存在就创建目录,使用if-then语句
    3,然后将磁盘使用情况写到指定文件中,最后删除1年之前的文件

并将磁盘使用情况写入文件

#!/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 -f

2 统计IP访问量

1,题目要求
有日志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命令
sort如果排序的对象是数字,我们可以加上-n,字符串的话不用加,Ip地址我们当作字符串来看,如果需要用uniq -c统计重复次数,必须先进行排序。
sort -n -r 对次数进行倒序排列,次数由大到小awk'

awk '{print $1}' 1.log|sort |uniq -c |sort -nr

3 统计所有进程占内存之和

题目要求
写一个脚本计算一下linux系统所有进程占用内存大小的和。

解析:
核心要点:
ps命令用法
for循环
加法运算
参考答案:

法一:
#!/bin/bash
sum=0
for n in `ps aux |grep -v 'TIME COMMAND' |awk '{print $6}'` 
do 
sum=$[$sum+$n]
done
echo $sum


法二:
#!/bin/bash
sum=0
for n in `ps aux |sed '1'd |awk '{print $6}'` 
do 
sum=$[$sum+$n]
done
echo $sum

法四:
ps aux --no-headers | awk '{sum+=$6} END {print sum}'

法五:
ps aux |awk '{if(NR>1){print $6}}' 
//NR:行号,当前处理的文本行的行号
//NF:number of filed 当前行的字段的个数,(即当前行被分割成了几列),字段数量怕

4 检测机器存活

题目要求
设计一个脚本,监控远程的一台机器(假设ip为180.163.26.39)的存活状态,当发现宕机时发一封邮件给你自己。
核心要点
ping -c10 180.163.26.39 通过ping来判定对方是否在线
参考答案:

#!/bin/bash
n=`ping -c5 192.168.81.112 |grep 'packet' |awk -F '%' '{print $1}' |awk '{print $NF}'`
if [ -z "$n"]
then
    echo "脚本有问题"mm
    python mail.py $m "检测机器存活脚本$0有问题""获取变量的值为空"
    exit
else 
    n1=`echo $n|sed 's/[0-9]//g'`
    if [ -n "$n" ]
    then
        echo "脚本有问题"
        python mail.py $m "检测机器存活脚本$0有问题" "获取的变量的值不是纯数字"
        exit
     fi          
fi   

if
m=123@qq.com
while :
do
     if [ $n -ge 50 ]
     then 
         python mail.py $m "机器宕机,丢包率是$n%"
     fi
     sleep 30
done 

5 批量修改并打包文件

题目要求
找到/123目录下所有后缀名为.txt的文件
批量修改.txt为.txt.bak
把所有.bak文件打包压缩为123.tar.gz
批量还原文件的名字,即把增加的.bak再删除
参考答案:

#!/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 {} {}.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      

6 检测80端口是否存在

【题目要求】
写一个脚本,判断本机的80端口(假如服务为httpd)是否开启,如果开启什么都不做,如果发现端口不存在,那么重启一下httpd服务,并发邮件通知自己,脚本写好后,可以每分钟执行一次,也可以写一个死循环,30s检测一次。

参考解析
思路:使用while-do-done语句实现循环功能,嵌套两个if-then语句实现过滤,告警功能
知识点:netstat -lntp ,wc -l,pgrep -lo,/usr/local/apache2/bin/apachectl -k restart,python mail.py $m,内容

#!/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

7 备份数据库

【题目要求】
设计一个shell脚本来备份数据库,首先在本地服务器上保存一份数据,然后再远程拷贝一份。本地保存一周的数据,远程保存一个月。
假定,我们知道mysql root账号的密码,要备份的库为discuz,本地备份目录为/bak/mysql,远程服务器ip为192.168.81.112,远程提供了一个rsync服务,备份的地址是192.168.81.112::backup,写完脚本后需要加入到cron中,每天凌晨3点执行。

核心要点
备份数据库的命令
同步到远程去的命令
本地一周,可以用date +%w做为后缀,远程一个月可以用date +%d做为后缀

#!/bin/bash
d1=`date +%w` //会产生一周7天的文件
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 backup begin at `date`"  //echo一个备份开始时间
mysqldump -uroot -pXXX discuz > $local_bakdir/discuz.sql.$d1
rsync -az $local_bakdir/discuz.sql.$d1 $remote_bakdir/discuz.sql.$d2 //-az,递归并压缩
echo "mysql backup end at `date`"   //echo一个备份结束时间

8 检测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 '`  //统计最后300行日志中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`   //统计pho-fpm的进程数
    if [ $fpm_p_n -eq 0 ]  //若为0
    then
        python mail.py xxx@xx.com "php-fpm重启失败" "`head -l /tmp/php-fpm.err`"  //发送告警邮件
        exit
    fi
fi
sleep 10
done

9 删除字母和行

题目要求:
把一个文本文档的前5行中包含字母的行删除掉,同时把6到10行中的全部字母删除掉。
核心要点:
sed命令
参考答案:

sed -n '1,5'p 1.txt |sed '/[a-zA-Z]/d'
sed '1,5d' 1.txt |sed '1,5s/[a-zA-Z]//g'

10 打印字母数小于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}'`  //以1个或多个空白字符或“-” “.”为分隔符,用来统计段数
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

11 输入数字执行命令

题目要求
写一个脚本实现如下功能: 输入一个数字,然后运行对应的一个命令。
显示命令如下:
cmd meau* 1 - date 2 - ls 3 - who 4 - pwd 当输入1时,会运行date, 输入2时运行ls, 以此类推。
核心要点
case判断
首先定义一个变量n,若为空则提示,退出;再定义一个非纯数字判断变量n1,若为非纯数字则提示退出,然后用case判断5种情况
参考答案

#!/bin/bash
echo "*cmd mena** 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

12 批量创建用户

题目要求
用shell脚本实现如下需求:
添加user_00 – user_09 10个用户,并且给他们设置一个随机密码,密码要求10位包含大小写字母以及数字,注意需要把每个用户的密码记录到一个日志文件里。 提示:
随机密码使用命令 mkpasswd
在脚本中给用户设置密码,可以使用echo 然后管道passwd命令
核心要点
seq实现数字递增
mkpasswd产生随机字符
首先使用for循环遍历00到09数字,seq -w 用来产生数字,mkpasswd -l -s 产生密码
然后将用户 密码记录到文件中
参考答案

#!/bin/bash
for i in `seq -w 00 09`  //-w:产生两位的数字
do
    useradd user_$i
    p=`mkpasswd -l 10 -s 0 `
    echo "user_$i $p" >> /tmp/pass.tmp  //将用户密码一一对应记录到文件中
    echo $p |passwd --stdin user_$i  //批量为用户设置密码,不需要输两遍密码确认,注意--stdin的使用
done

13 监控httpd进程

题目要求
在服务器上,写一个监控脚本,要求如下:
1,每隔10s去检测一次服务器上的httpd进程数,如果大于等于500的时候,就需要自动重启一下apache服务,并检测启动是否成功?
2,若没有正常启动还需再一次启动,最大不成功数超过5次则需要立即发邮件通知管理员,并且以后不需要再检测!
3,如果启动成功后,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 ]  //不等于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

14 封ip

题目要求
需求: 根据web服务器上的访问日志,把一些请求量非常高的ip给拒绝掉!并且每隔半小时把不再发起请求或者请求量很小的ip给解封。 假设:
一分钟内请求量高于100次的IP视为不正常请求。
访问日志路径为/data/logs/access_log。
日志演示:

核心要点
统计ip访问次数,排序
如何标记每隔半小时
iptables计数器是一个重要的判断指标
函数(封IP、解封IP)
定义两个函数,
一个block_ip函数:先定义一个一分钟之前的时间变量,然后使用egrep将一分钟以前的日志过滤出来,使用awk将ip地址过滤出来,排序,去重,排序,将大于100的IP地址过滤出来,并将其重定向到文本中,然后遍历其中的ip进行封禁
一个unblock_ip函数:我们以iptables -nvL中的请求数据包计数作为解封ip判断依据,若小于5则视为此ip恢复正常并对其解封
然后定义一个主脚本,每30分钟解封一次ip

参考答案:

#!/bin/bash
block_ip()  //封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  
//对第一列的ip地址进行排序去重再排序,并将大于100的ip过滤出来
n=`wc -l /tmp/bad_ip.list|awk '{print $1}'`  //计算bad_ip.list文件列表行数
if [ $n -ne 0 ]      //如果不等于0
then
    for ip in `cat /tmp/bad_ip.list`  //遍历IP地址
    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  //将被封ip数据包请求数小于5视为正常ip,将其过滤出来重定向到文档中
    n=`wc -l /tmp/good_ip.list|awk '{print $1}'`  //计算可以放行ip列表的行数
    if [ $n -ne 0 ]  //如果不为空
    then
    for ip in `cat /tmp/good_ip.list`  //遍历ip
    do
	iptables -D INPUT -s $ip -j REJECT  //将禁用ip规则删除
    done
    fi
    iptables -Z  //清空规则链中的数据包计算器和字节计数器;
}

t=`date +%M`
if [ $t == "00" ] || [ $t == "30" ]  //每30分钟解封一次ip
then
   unblock_ip  //先执行unblock函数
   block_ip
else
   block_ip
fi

15 算数字

题目要求
请仔细查看如下几个数字的规律,并使用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

16 查用户

题目要求:
写个shell,看看你的Linux系统中是否有自定义用户(普通用户),若是有,一共有几个?
核心要点:
Centos 6,uid>=500
Centos 7,uid>=1000
首先判断linux的版本(cat /etc/redhat-release),然后自定义统计自定义用户的行数
方法一:

#!/bin/bash
v=`awk -F 'release ' '{print $2}' /etc/redhat-release |cut -d '.' -f1`
case $v in
  5|6)
     n=`awk -F ':' '$3>=500' /etc/passwd |wc -l`
     if [ $n -eq 0 ]
     then
         echo "系统没有自定义的用户"
      else
         echo "系统存在自定义用户,有$n个"
     fi
   ;;
   7)
     n=`awk -F ':' '$3>=1000' /etc/passwd |wc -l`
     if [ $n -eq 0 ]
     then
         echo "系统没有自定义的用户"
     else
         echo "系统存在自定义用户,有$n个"
     fi
   ;;
   *)
      echo "脚本出错"
   ;;
esac

方法二:定义一个user函数,简化脚本

#!/bin/bash
v=`awk -F 'release ' '{print $2}' /etc/redhat-release |cut -d '.' -f1`
user()
{     
     if [ $1 -eq 0 ]
     then
         echo "系统没有自定义的用户"
      else
         echo "系统存在自定义用户,有$1个"
     fi
}
case $v in
  5|6)
     n=`awk -F ':' '$3>=500' /etc/passwd |wc -l`
     user $n    
   ;;
   7)
     n=`awk -F ':' '$3>=1000' /etc/passwd |wc -l`
     user $n
   ;;
   *)
      echo "脚本出错"
   ;;
esac

17 检测磁盘并邮件告警

题目要求
写一个shell脚本,检测所有磁盘分区使用率和inode使用率并记录到以当天日期为命名的日志文件里,当发现某个分区容量或者inode使用量大于85%时,发邮件通知你自己。
思路:
首先定义日志文件目录变量,时间变量,邮箱地址变量,然后判断日志文件目录是否存在,若不存在则创建一个日志文件目录,然后将df,df -i 内容追加到日志中,此时完成需求1。
然后对df,df -i 进行过滤,将使用量大于85的行重定向到临时文件中,然后计算出临时文件中行数并打印出行数,然后定义一个tag变量,使用if-then结构对四种情况进行处理,然后使用case结构进行邮件相关操作

参考答案:

#!/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  //磁盘分区使用率若大于85就打印出分区名称
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`"  //xargs目的是将文本写道一行
	;;
    *)
	;;
esac

18 检测新文件

题目要求:
有一台服务器作为web应用,有一个目录(/data/web/attachment)不定时地会被用户上传新的文件,但是不知道什么时候会上传。所以,需要我们每5分钟做一次检测是否有新文件生成。请写一个shell脚本去完成检测。检测完成后若是有新文件,还需要将新文件的列表输出到一个按年、月、日、时、分为名字的日志里。
思路:
定义原目录,和时间变量,用find -mmin -5将5分钟内的文件重定向到临时文件列表中,然后计算文件列表行数,若大于0,则将临时文件列表改名为以时间标记的文件中
参考:

#!/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 -gt 0 ]
then
    mv /tmp/file.list /tmp/$t.list
fi

19 列出最常用的10个命令

题目:
写一个shell脚本来看看你最常用命令是哪些,列出最常用的命令top 10

cat ~/.bash_history |sort |uniq -c |sort -nr |head

20 统计文件大小

题目:
假如需要每小时都去执行一个脚本,在脚本中实现这样的功能,当时间时0点和12点时,需要将目录/data/log/下的文件全部清空,注意只能清空文件内容而不能删除文件。而其它时间只需要统计一下每个文件的大小,一个文件一行,输出到一个按日期和时间为名字的日志里,需要考虑/data/log/目录下二级,三级...等子目录里面的文件。

参考:

#!bin/bash
dir=/tmp/log_stat
t=`date +%d%H`
t1=`date +%H`
logdir=/data/log

[ -d $dir ] || mkdir $dir
[ -f $dir/$t.log ] && rm -f $dir/$t.log

if [ $t1 == "00" -o $t1 =="12" ]
then
    for f in `find $logdir/ -type f`
    do
       > $f
    done
else
     for f in `find $logdir/ -type f`
     do
         du -sh $f >> $dir/$t.log
     done
fi

21 计算数字个数

题目:
计算文档a.txt中每一行中出现的数字个数并且要计算一下整个文档中一共出现了几个数字。例如a.txt内容如下: 12aa*lkjskdj alskdflkskdjflkjj 我们脚本名字为 ncount.sh, 运行它时: bash ncount.sh a.txt 输出结果应该为: 2 0 sum:2
参考:

#!/bin/bash
sum=0
while read line
do
    line_n=`echo $line|sed 's/[^0-9]//g'|wc-L`
    echo $line_n
    sum=$[$sum+$line_n]
done < $1
echo "sum:$sum"

22_对比文件差异

题目:
有两台linux服务器A和B,假如A可以直接ssh到B,不用输入密码。A和B都有一个目录叫做/data/web/这下面有很多文件,当然我们不知道具体由几层子目录,假若之前A和B上该目录下的文件都是一模一样的。
但现在不确定是否一致了。故需要我们写一个脚本实现这样的功能,检测A机器和B机器/data/web/目录下文件的异同,我们以A机器上的文件作为标准。
比如,假若B机器少了一个a.txt文件,那我们应该能检测出来,或者B机器上的b.txt文件有过改动,我们也应该能够检测出来(B机器上多个文件不应考虑)。

参考:

#!/bin/bash
dir=/data/web
[ -f /tmp/md5.list ] && rm -f /tmp/md5.list
find $dir/ -type f > /tmp/file.list
while read line 
do
    md5sum $line  >> /tmp/md5.list
done < /tmp/file.list

scp /tmp/md5.list B:/tmp/
[ -f /tmp/check_md5.sh ] && rm -f /tmp/check_md5.sh

cat >/tmp/check_md5.sh << EOF
#!/bin/bash
dir=/data/web
n=\`wc -l /tmp/md5.list|awk '{print \$1}'\`
for i in \`seq 1 \$n\`
do
    file_name=\`sed -n "\$i"p /tmp/md5.list |awk '{print \$1}'\`
    md5=\`sed -n "\$i"p /tmp/md5.list|awk '{print \$2}'\`
    if [ -f \$file_name ]
    then
	md5_b=\`md5sum \$file_name\`
	if [\$md5_b != \$md5 ]
	then
	    echo "\$file_name changed."
	fi
    else
	echo "\$file_name lose."
    fi
done
EOF
scp /tmp/check_md5.sh B:/tmp/
ssh B "/bin/bash /tmp/check_md5.sh"

题目要求
写一个脚本,检测你的网络流量,并记录到一个日志里。需要按照如下格式,并且一分钟统计一次(只需要统计外网网卡,假设网卡名字为eth0):
2017-08-04 01:11 eth0 input: 1000bps eth0 output : 200000bps
2017-08-04 01:12 eth0 input: 1000bps eth0 output : 200000bps
提示:使用sar -n DEV 1 59 这样可以统计一分钟的平均网卡流量,只需要最后面的平均值。另外,注意换算一下,1Byte=8bit

23 检测网卡流量

题目要求
写一个脚本,检测你的网络流量,并记录到一个日志里。需要按照如下格式,并且一分钟统计一次(只需要统计外网网卡,假设网卡名字为eth0):
2017-08-04 01:11 eth0 input: 1000bps eth0 output : 200000bps
2017-08-04 01:12 eth0 input: 1000bps eth0 output : 200000bps
提示:使用sar -n DEV 1 59 这样可以统计一分钟的平均网卡流量,只需要最后面的平均值。另外,注意换算一下,1Byte=8bit

参考答案

#!/bin/bash
logdir=/tmp/sar_log
file=$logdir/`date +%d%H`.log
t=`date +"%F %H:%M"`

[ -d $logdir ] || mkdir -p $logdir
LANG=en
sar -n DEV 1 5 |grep eth0 |grep "Average" > /tmp/sar.tmp

exec >>$file
echo "$t"
awk '{print "etho input:",$5*8000"bps""\n""eth0 output:",$6*8000"bps"}'
echo "#######################"

24 批量杀进程

题目要求
一台机器负载高,top查看有很多sh的进程,然后top -c查看可以看到对应的进程命令是sh -c /bin/clearnen.sh 。
经分析后发现是因为该脚本执行时间太长,导致后续执行时,上次的脚本还未执行结束。写一个脚本批量杀死所有sh的进程。

参考:

#!/bin/bash
for pid in `ps aux |grep clearnen.sh |awk '{print $2}'`
do 
   echo $pid
   kill -9 $pid
done

25 判断是否开启了web服务

题目要求
写一个脚本判断你的Linux服务器里是否开启web服务?(监听80端口)如果开启了,请判断出跑的是什么服务,是httpd呢还是nginx又或者是其他的什么?
参考答案

#!/bin/bash
n=`netstat -lntp |grep ':80'|wc -l`
if [ $n -eq 0 ]
then
    echo "it not listen port 80"
else
    ser=`netstat -lntp |grep ':80'|awk -F '[/:]' '{print $4}'`
    echo "it is listening port 80,and the service is $ser."
fi   

26 监控mysql服务

题目要求
假设,当前MySQL服务的root密码为123456,写脚本检测MySQL服务是否正常(比如,可以正常进入mysql执行show processlist),
并检测一下当前的MySQL服务是主还是从,如果是从,请判断它的主从服务是否异常。如果是主,则不需要做什么。

参考答案

#!/bin/bash
mysql="/usr/local/mysql/bin/mysql -uroot -p123456"
if ! $mysql -e "show processlist" >/dev/null 2>/dev/null   //看此命令是否成功执行,不关心输出什么内容
then
    echo "MySQL service is down."
    exit
else
     $mysql -e "show slave status\G" 2>/dev/null >/tmp/slave.stat  //判断是否有输出
     n=`wc -l /tmp/slave.stat|awk '{print $1}'`  //计算临时文件行数
    if [ $n -eq 0 ]    
    then
	     echo "This is master."
    else
	     echo "This is slave."
	     egrep 'Slave_IO_Running:|Slave_SQL_Running:'/tmp/slave.stat|awk -F ': ' '{print $2}' > /tmp/SQL.tmp  //根据IO,SQL_Runnig判断从是否有问题
	     if grep -qw "No" /tmp/SQL.tmp   //如果有一个是NO,则说明有问题
	     then
	     echo "The slave is down."
	     fi
    fi
fi  

注:

grep
-q 静默模式,不输出任何信息,当我们只关系有没有匹配到,却不关心匹配到什么内容的时候,我们可以使用此命令,然后,使用"echo $?"查看是否匹配到,0表示匹配到,1表示没有匹配到;
-w 匹配整个单词,如果是字符串中包含这个单词,则不作匹配;

27 增删用户

题目要求:
写一个支持选项的增加或删除用户的shell脚本,具体要求如下:
只支持三个选项:'--del','--add','--help',输入其他选项报错。
使用'--add'时,需要验证用户名是否存在,存在则反馈存在,且不添加。 不存在则创建该用户,需要设置与该用户名相同的密码。
使用'--del'时,需要验证用户名是否存在,存在则删除用户及其家目录。不存在则反馈该用户不存在。
--help选项反馈出使用方法。
能用echo $?检测脚本执行情况,成功删除或添加用户为0,不成功为非0正整数。
能以英文逗号分割,一次性添加或者删除多个用户。例如 adddel.sh --add user1,user2,user3

#!/bin/baash
if [ $# -eq 0 ] || [ $# -gt 2 ]
then
    echo "Wrong, use bash $0 --add username, or bash $0 --del username or bash $0 --help"
    exit
fi

ex_user()
{
    if ! id $1 2>/dev/null >/dev/null
    then
	useradd $1 && echo "$1 add successful."
    else
	echo $1 exist.
    fi
}

notex_user()
{
    if id $1 2>/dev/null >/dev/null
    then
	userdel $1 && echo "$1 delete successful."
    else
	echo $1 not exist.
    fi
}
	

case $1 in 
    --add)
	if [ $# -eq 1 ]
	then
	    echo "Wrong, use bash $0 --add user or bash	$0 --add user1,user2,user3..."
	    exit
	else
	    n=`echo $2| awk -F ',' '{print NF}'`
	    if [ $n -gt 1 ]
	    then
	        for i in `seq 1 $n`
		do
		    username=`echo $2 |awk -v j=$i -F ',' '{print $j}'`
		    ex_user $username
	        done
	    else
		ex_user $2
	    fi
	fi
	;;
    --del)
	if  [ $# -eq 1 ]
        then
            echo "Wrong, use bash $0 --del user or bash $0 --del user1,user2,user3..."
            exit
        else
            n=`echo $2| awk -F ',' '{print NF}'`
            if [ $n -gt 1 ]
            then
                for i in `seq 1 $n`
                do
                    username=`echo $2 |awk -v j=$i -F ',' '{print $j}'`
		    notex_user $username
                done
            else
		notex_user $2
            fi
        fi
        ;;
    --help)
        if  [ $# -ne 1 ]
        then
            echo "Wrong, use bash $0 --help"
            exit
        else

	echo "Use bash $0 --add username or bash $0 --add user1,user2,user3... add user."
	echo "    bash $0 --del username -r bash $0 --del user1,user2,user3... delete user."
	echo "    bash $0 --help print this info."
	fi
    ;;
    *)
	echo "Wrong, use bash $0 --add username, or bash $0 --del username or bash $0 --help"
    ;;
esac

28 计算数字和

题目要求
写一个脚本: 计算100以内所有能被3整除的正整数的和

解析:

#!/bin/bash
sum=0
for i in `seq 1 100`
do
    j=$[$i%3]    //通过%进行取余操作,然后进行判断
    if [ $j -eq 0 ]
    then 
        sum=$[$sum+$i]
    fi
done
echo $sum     

29 加减乘除

题目要求
使用传参的方法写个脚本,实现加减乘除的功能。 例如: sh a.sh 1 2,这样会分别计算加、减、乘、除的结果。
要求:
脚本需判断提供的两个数字必须为整数
当做减法或者除法时,需要判断哪个数字大,减法时需要用大的数字减小的数字,除法时需要用大的数字除以小的数字,并且结果需要保留两个小数点。
解析:

#!/bin/bash
is_nu()
{
    n=`echo $1 |sed 's/[0-9]//g'`
    if [ -n "$n" ]
    then
	echo "给出的参数必须是正整数"
        exit
    fi
}
if [ $# -ne 2 ]
then
    echo "必须要输入两个参数"
    exit
else
    is_nu $1
    is_nu $2
fi

big()
{
    if [ $1 -gt $2 ]
    then
	echo $1
    else
	echo $2
    fi
}

small()
{
    if [ $1 -lt $2 ]
    then
        echo $1
    else
        echo $2
    fi
}

add()
{
    sum=$[$1+$2]
    echo "$1+$2=$sum"
}

jian()
{
   b=`big $1 $2`
   s=`small $1 $2`
   cha=$[$b-$s]
   echo "$b-$s=$cha"
}

cheng()
{
    ji=$[$1*$2]
    echo "$1x$2=$ji"
}
chu()
{
   b=`big $1 $2`
   s=`small $1 $2`
   v=`echo "scale=2;$b/$s"|bc`
   echo "$b/$s=$v"
}

add $1 $2
jian $1 $2
cheng $1 $2
chu $1 $2

30 输入数字

题目要求
写一个脚本,执行后,打印一行提示“Please input a number:",要求用户输入数值,然后打印出该数值,然后再次要求用户输入数值。直到用户输入"end"停止。

#!/bin/bash
while :
do
    read -p "Please input a number: " n
    if [ -z "$n" ]
    then
	echo "请输入一个纯数字."
	continue
    fi
    if echo $n |grep -qi 'end'
    then
	exit
    fi
    n1=`echo $n|sed 's/[0-9]//g'`
    if [ -n "$n1" ]
    then
	echo "请输入一个纯数字."
        continue
    else
	echo "你输入的数字是: $n"
        continue
    fi
done

31 获取网卡ip并显示颜色

题目要求
提示用户输入网卡的名字,然后我们用脚本输出网卡的ip,并要求输出的ip显示颜色,需要考虑下面问题:
输入的字符不符合网卡名字规范,怎么应对。
名字符合规范,但是根本就没有这个网卡又怎么应对。

[root@ser02:~]# cat net.sh 
#!/bin/bash
ip add |awk -F ': ' '$1 ~ "^[1-9]" {print $2}' > /tmp/eth.list
while :
do 
     eths=`cat /tmp/eth.list|xargs`
     read -p "Please input a if name(The eths is `echo -e "\033[31m$eths\033[0m"`): " eth
     if [ -z "$eth" ]
     then 
         echo "Please input a if name."
         continue
     fi
     if ! grep -qw "$eth" /tmp/eth.list  //-q 静默模式,-w 匹配整个单词
     then
         echo "The if name is error."
         continue
     else
         break
     fi
done

if_ip()
{
     ip add show dev $1|grep ' inet '|awk '{print $2}'|awk -F '/' '{print $1}' >/tmp/$1.txt
     n=`wc -l /tmp/$1.txt|awk '{print $1}'`
     if [ $n -eq 0 ]
     then
         echo "There is no ip address on then eth."
     else
         echo "The ip address is:"
         for ip in `cat /tmp/$1.txt`
         do
            echo -e "\033[33m$ip\033[0m"
         done
     fi
}

if_ip $eth


32 列出目录内容

题目要求
写一个脚本,实现如下功能:
脚本可以带参数也可以不带
参数可以有多个,每个参数必须是一个目录
脚本检查参数个数,若等于0,则列出当前目录本身,否则显示每个参数包含的子目录。
参考答案

#!/bin/bash
if [ $# -eq 0 ]
then 
    echo "当前目录下的文件是:"
    ls .
else
    for d in $@  //$@表示所有参数   
    do
         if [ -d $d ]
        then
              echo "目录$d下有这些子目录:"
              find $d -type d
        else
              echo "并没有目录:$d"
        fi
    done
fi 

33 下载文件

题目要求
定义一个shell函数,能接受两个参数,满足以下要求:
第一个参数为URL,即可下载的文件,第二个参数为目录,即下载后保存的位置
如果用户给的目录不存在,则提示用户是否创建,如果创建就继续执行,否则,函数返回一个51的错误值给调用脚本
如果给的目录存在,则下载文件,下载命令执行结束后测试文件下载成功与否,如果成功,则返回0给调用脚本,否则,返回52给调用脚本
解析:

#!/bin/bash
if [ $# -ne 2 ]
then
    echo "你必须输入两个参数,第一个参数是网址,第二个参数是目录."
    exit 1
fi

if [ ! -d $2 ]
then
    while :
    do
    echo "你输入的第二个参数,并不是一个存在的目录。是否要创建该目录呢?(y|n): "c
    case $c in
         y|Y)
             mkdir -p $2
             ;;
          n|N)
             exit 51
             ;;
          *)
             echo "请输入y或者n."
             continue
             ;;
     esac
     done
else
      cd $2
      wget $2
      if [ $? -eq 0 ]
      then 
          exit 0
      else
          echo "下载失败."
          exit 52
      fi
fi     

34 猜数字

题目要求
写一个猜数字脚本,当用户输入的数字和预设数字(随机生成一个0-100的数字)一样时,直接退出,否则让用户一直输入,并且提示用户的数字比预设数字大或者小。
解析:

#!bin/bash
n=$[$RANDOM%101] //$RANDOM可以产生一个随机数字
while :
do 
   read -p "请输入一个0-100的数字:" n1
   if [ -z "$n1" ]
   then
       echo "必须要输入一个数字"
       continue
   fi 
   n2=`echo $n1 |sed 's/[0-9]//g'`
   if [ -n "$n2" ]
   then
       echo "你输入的数字并不是正整数."
       continue
   else
       if [ $n -gt $1 ]
       then
           echo "你输入的数字小了,请重试"
           continue
       elif [ $n -lt $n1 ]
       then
            echo "你输入的数字大了,请重试。"
            continue
        else
            echo "恭喜你,猜对了!"
            break
        fi
    fi
done

35 根据名字得出数字

题目要求:
写一个shell脚本,能实现如下需求:
执行脚本后,提示输入名字(英文的,可以是大小写字母、数字不能有其他特殊符号),然后输出一个随机的0-99之间的数字,脚本并不会退出,继续提示让输入名字
如果输入相同的名字,输出的数字还是第一次输入该名字时输出的结果
前面已经输出过的数字,下次不能再出现
当输入q或者Q时,脚本会退出。
参考:
判断名字合法性[a-zA-Z0-9]
$RANDOM处以100取余得到0-99之间的随机数

#!/bin/bash
f=/tmp/user_number.txt
j_n()
{
    while :
    do
	n=$[RANDOM%100]
	if awk '{print $2}' $f|grep -qw $n
	then
	    continue
	else
	    break
	fi
    done
}

while :
do
    read -p "Please input a username: " u
    if [ -z "$u" ]
    then
	echo "请输入用户名."
	continue
    fi

    if [ $u == "q" ] || [ $u == "Q" ]
    then
	exit
    fi 

    u1=`echo $u|sed 's/[a-zA-Z0-9]//g'`
    if [ -n "$u1" ]
    then
	echo "你输入的用户名不符合规范,正确的用户名应该是大小写字母和数字的组合"
	continue
    else
	if [ -f $f ]
	then
	    u_n=`awk -v uu=$u '$1==uu {print $2}' $f`
	    if [ -n "$u_n" ]
	    then
		echo "用户$u对应的数字是:$u_n"
	    else
		j_n
		echo "用户$u对应的数字是:$n"
	        echo "$u $n" >>$f
	    fi
	else
	    j_n
	    echo "用户$u对应的数字是:$n"
	    echo $u $n >> $f
	fi
    fi
done

36 一个数字的行

1,用shell实现,把一个文本文档中只有一个数字的行给打印出来。

核心要点:用sed替换所有非数字的字符为空,剩下的就是数字

#!/bin/bash
while read line  //遍历文件所有的行
do 
   n=`echo $line|sed  's/[^0-9]//g'|wc -L  //将所有非数字的字符替换为空,wc -L计算字符串长度
   if [ $n -eq 1 ]
   then 
        echo $line
   fi
done < 1.txt //指定一个待操作的文件

37 日志切割分档

题目要求
系统logrotate工具,可以完成日志切割、归档。写一个shell脚本实现类似的归档功能。 举例: 假如服务的输出日志是1.log,要求每天归档一个,1.log第二天就变成1.log.1,第三天1.log.2, 第四天 1.log.3 一直到1.log.5。

#!/bin/bash
cd /data/logs
log=1.log
mv_log()
{
    [ -f $1 ] && mv $1 $2
}
[ -f 1.log.5 ] && rm -f 1.log.5
for i in `seq 4 -1 1`
do
    j=$[$i+1]
    mv_log $log.$i $log.$j
done
mv 1.log 1.log.1

38 查找在线IP

题目:
写一个shell脚本,把192.168.0.0/24网段在线的ip列出来。

参考答案:

#!/bin/bash
for i in `seq 1 254`
do 
    if ping -c 2 -W 2 192.168.0.$i >/dev/null 2>/dev/null  //-c ping的次数,-W 2 定义超时时间
    then 
        echo "192.168.0.$i 是通的"
    else
        echo "192.168.0.$i 不通"
    fi
done

39 检查脚本错误

题目:
写一个shell脚本,检查指定的shell脚本是否有语法错误,若有错误,首先显示错误信息,然后提示用户输入q或Q退出脚本,输入其它内容则直接用vim打开该shell脚本。

参考解析:

#!/bin/bash
sh -n $1 2>/tmp/sh.err
if [ $? -ne 0 ]  //如果$?不等于0
then 
     cat /tmp/sh.err
     read -p "请输入q/Q退出脚本。 " c  //定义一个指令c
     if [ -z "$c" ]
     then
          vim $1
         exit 0
    fi
    if [ $c == q ] || [ $c == Q ]
    then
        exit 0
    else
        vim $1
        exit 0
    fi
 else
    echo 脚本$1没有语法错误"
fi 

40 格式化数字

题目要求
输入一串随机数字,然后按千分位输出。 比如输入数字串为“123456789”,输出为123,456,789。
解析:

#!/bin/bash
n=`echo $1|wc -L`
for d in `echo $1|sed 's/./& /g'`  //.代表每个数字,&代表前面的.
do
    n2=$[$n%3]  //判断是否能被3整除
    if [ $n2 -eq 0 ]
    then 
        echo -n ",$d"   //echo -n 不换行输出,能被3整除就在前面加一个,
    else
       echo -n "$d"
   fi
   n=$[$n-1]
done |sed 's/^,//'
echo

41 问候脚本

题目要求
编写一个问候程序,它执行时能根据系统当前的时间向用户输出问候信息。假设从半夜到中午为早晨,中午到下午六点为下午,下午六点到半夜为晚上。
参考解析:

#!/bin/bash
d=`date +%H`
if [ $d -ge 0 -a $d -lt 7 ]
then
    tag=1
elif [ $d -ge 7 -a $d -lt 12 ]
then
    tag=2
elif [ $d -ge 12 -a $d -lt 18 ]
then
   tag =3 
else
   tag=4
fi
case $tag in
1)
     echo "早上好"
     ;;
2)
     echo "上午好"
     ;;
3)   
     echo "下午好"
     ;;
4) 
     echo "晚上好"
     ;;
*)
     echo "脚本出错啦"
     ;;
esac

42 菜单脚本

题目要求
写一个shell脚本,实现简单的弹出式菜单功能,用户能根据显示的菜单项从键盘选择执行对应的命令。

法1:select do done 循环体

#!/bin/bash
PS3="Please input your choice(1-4): "
select i in w ls pwd quit
do
    case $i in
         w)
             w
             ;;
         ls)
            ls
            ;;
        pwd)
            pwd
            ;;
        quit)
            exit
            ;;
        *)
            echo "Please input 1-3."
            ;;
      esac
done

法2:

#!/bin/bash
echo -e "1) w\n2) pwd\n4) quit"
while :
do
read -p "Please input your choice(1-4): " c
case $c in
     1)
        w
        ;;
    2)
       ls
       ;;
    3)
       pwd
       ;;
    4 )
       exit
       ;;
    *)
       echo "Please input 1-4."
       ;;
esac
done

43 检查用户是否登陆

题目要求:
写一个shell脚本,执行中每隔5分钟检查指定的用户是否登录系统,用户名从命令行输入,如果指定的用户已经登录,则显示相关信息。

思路:利用传参的形式,w命令进行

[root@ser03:~]# vim login.sh

#!/bin/bash 
while :
do
   if w|awk 'NR>2 {print $1}'|grep -qw "$1" 
   //首先过滤出来的第一列,看看能不能搜到传参参数$1,也就是用户名
   then
       echo "用户$1已经登录系统."
       exit
   fi
   sleep 300
done
                
测试:
sh login.sh user1

44 检查系统是否被入侵

1,先普及一个小常识,我们用ps aux可以查看到进程的PID,而每个PID都会在/proc内产生。如果查看到的pid在proc内是没有的,则进程被人修改了,这就代表系统很有可能已经被入侵过了。 请用上面知识编写一个shell,定期检查下自己的系统是否被人入侵过

#!/bin/bash
pp=$$
ps -elf |sed  '1'd>/tmp/pid.txt
for pid in `awk -v ppn=$pp '$5!=ppn {print $4}' /tmp/pid.txt`
do 
    if ! [ -d /proc/$pid ]
    then 
        echo "系统中并没有pid为$pid的目录,需要检查"
    fi
done

45 三行并一行

题目要求
想办法把文本里面每三行内容合并到一行 例如:1.txt内容
1
2
3
4
5
6
7

处理后应该是
1 2 3
4 5 6
7

思路:while read line逐行遍历,echo -n :不换行输出

参考答案

#!/bin/bash
n=1
cat $1 |while read line
do
   n1=$[$n%3]  //取余
   if [ $n1 -eq 0 ]
   then
       echo "$line "
   else
       echo -n "$line " 
  fi
  n=$[$n+1]
done

46 网卡和ip

题目要求
写一个getinterface.sh 脚本可以接受选项[i,I],完成下面任务:
1)使用格式:getinterface.sh [-i interface | -I ip]
2)当用户使用-i选项时,显示指定网卡的IP地址;当用户使用-I选项时,显示其指定ip所属的网卡。
例: sh getinterface.sh -i eth0 或者
sh getinterface.sh -I 192.168.0.1
3)当用户使用除[-i | -I]选项时,显示[-i interface | -I ip]此信息。
4)当用户指定信息不符合时,显示错误。(比如指定的eth0没有,而是eth1时)

参考答案:

#!/bin/bash
ip add |awk -F ': ' '$1 ~ "^[1-9]" {print $2}' > /tmp/ifs.txt   
get_ip()
{
    ip add show dev $1 |grep inet |awk '{print $2}' |awk -F '/' '{print $1}'
}

for eth in `cat /tmp/ifs.txt`
do
    myip=`get_ip $eth`
    if [ -z "$myip" ]
    then
	echo $eth 
    else
	echo $eth $myip 
    fi
done > /tmp/if_ip.txt 

if [ $# -ne 2 ]  //判断用户输入的变量是不是2个
then
    echo "请输入正确的格式: bash $0 -i 网卡 或者 bash $0 -I ip"
    exit
fi

if [ $1 == "-i" ] //第一个参数是不是i
then
    if awk '{print $1}' /tmp/if_ip.txt |grep -qw $2  //
    then
	eth=$2
	ip1=`awk -v aeth=$eth '$1==aeth' /tmp/if_ip.txt|sed "s/$eth //"`
        echo "网卡$2的ip是 $ip1"
    else
	echo "你指定的网卡不对,系统中的网卡有:`cat /tmp/ifs.txt|xargs`"
	exit
    fi
elif [ $1 == "-I" ]
then
    if grep -qw " $2 "  /tmp/if_ip.txt
    then
	eth=`grep -w " $2 " /tmp/if_ip.txt|awk '{print $1}'`
        echo "IP $2对应的网卡是$eth"
    else
	echo "你指定的ip不对,系统中的IP有:`ip add |grep inet |awk '{print $2}'|awk -F '/' '{print $1}'|xargs`"
	exit
    fi
else
    echo "请输入正确的格式: bash $0 -i 网卡 或者 bash $0 -I ip"
fi

47 随机3位数

题目要求
写一个脚本产生随机3位的数字,并且可以根据用户的输入参数来判断输出几组。 比如,脚本名字为 number3.sh。
执行方法:
1)bash number3.sh 会产生一组3位数字。
2)bash number3.sh 10 会产生10组3位数字。

#!/bin/bash
get_number()
{
    for i in `seq 0 2`
    do 
        a[$i]=$[$RANDOM%10]
    done 
    echo ${a[@]}|sed s'/ //g'
}

if [ $# -eq 0 ]
then
    get_number
elif [ $# -eq 1 ]
then
    n=`echo $1|sed 's/[0-9]//g'`
    if [ -n "$n" ]
    then
	echo "给定的参数必须是一个数字"
        exit
    fi
    for i in `seq 1 $1`
    do
        get_number
    done |xargs 
else
    echo "格式不对,正确的是格式是sh $0 [n],这里的n是一个数字。"
fi

48 是否安装包

题目要求
写一个shell,先判断是否安装httpd和mysql,没有安装进行安装,安装了检查是否启动服务,若没有启动则需要启动服务。

#!/bin/bash
if_install()
{
    rpm -q $1 >/dev/null 2>/dev/null
    if [ $? -eq 0 ]
    then
        echo "$1已经安装"
        return 0
    else
       echo "$1没有安装"
       return 1
   fi
}
if_install httpd
if [ $? -eq 0 ]
then
    if ! pgrep httpd >/dev/null
    then
        service httpd start
    fi
else
    yum install -y httpd
fi

if_install mysql-server
if [ $? -eq 0 ]
then
    if ! pgrep mysqld start
    then
        service mysqld start
    fi
else
    yum install -y mysql-server
fi

题目要求
用shell脚本判断输入的日期是否合法。
比如20170110就是合法日期,20171332就不合法。

#!/bin/bash
if [ $# -ne 1 ] || [ ${#1} -ne 8 ]
then
    echo "请输入正确的格式,sh $0 yyyymmdd"
    exit 1
fi

y=`echo ${1:0:4}`
m=`echo ${1:4:2}`
d=`echo ${1:6:2}`

if echo $d|grep -q "^0"
then
    d=`echo ${1:6:2}|sed 's/^0//'`
fi

if cal $m $y >/dev/null 2>/dev/null
then
     if ! cal $m $y|grep -qw "$d"
     then 
         echo "你给的日期是不合法的"
     else
         echo "日期合法"
     fi
else
    echo "你给的日期不合法"
fi    

49 日期是否合法

题目要求
用shell脚本判断输入的日期是否合法。
比如20170110就是合法日期,20171332就不合法。

#!/bin/bash
if [ $# -ne 1 ] || [ ${#1} -ne 8 ]
then
    echo "请输入正确的格式,sh $0 yyyymmdd"
    exit 1
fi

y=`echo ${1:0:4}`
m=`echo ${1:4:2}`
d=`echo ${1:6:2}`

if echo $d|grep -q "^0"
then
    d=`echo ${1:6:2}|sed 's/^0//'`
fi

if cal $m $y >/dev/null 2>/dev/null
then
     if ! cal $m $y|grep -qw "$d"
     then 
         echo "你给的日期是不合法的"
     else
         echo "日期合法"
     fi
else
    echo "你给的日期不合法"
fi 

50 监控流量

题目要求
写一个监控网卡的脚本,需要满足以下要求:
每10分钟检测一次指定网卡的流量。
如果流量为0,则重启网卡。

#!/bin/bash
LANG=en
sar -n DEV 1 10|grep -w "$1" > /tmp/sar.tmp
in=`grep "Average:" /tmp/sar.tmp|awk '{print $5}'|sed 's/\.//'`
out=`grep "Average:" /tmp/sar.tmp|awk '{print $6}'|sed 's/\.//'`

if [ $in == "000"] && [ $out == "000" ]
then
    ifdown $1
    ifup $1
fi

51 判断网站是否正常

题目要求:
写一个shell脚本,通过curl -I 返回的状态码来判定所访问的网站是否正常。比如,当状态码为200时,才算正常。

#!/bin/bash
#这个脚本用来判定一个网站是否正常
#作者:倪实
#日期:2020-04-09
url="http://www.nsthink.com/index.php"
mail_user=123456@qq.com

code=`curl -I $url 2>/tmp/curl.err|head -l|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 

52 打包小于5k的文件

题目要求
将用户家目录(考虑到执行脚本的用户可能是普通用户也可能是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`

54 分析日志

题目要求
已知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:[01]:[0-5]:[0-9]:`

egrep "$t" $log|awk '{print $1}' |sort -n |uniq -c|sort -n|tail -1|awk '{print $2}'

55 打印数字

题目要求
写一个shell脚本。提示输入一个暂停的数字,然后从1打印到该数字。然后询问是否继续。继续的话再输入一个数字接着打印,否则退出脚本。
例:如果输入的是5,打印1 2 3 4 5,然后继续输入15,然后打印6 7 …14 15 以此类推。
参考答案

#!/bin/bash
#这个脚本用来打印数字


read -p "Please input a number: " n
n1=`echo $n |sed 's/[0-9]//g'`
if [ -n "$n1" ]
then
    echo "Please input a number."
    exit
fi

for i in `seq 1 $n`
do
    echo $i
done

read -p "If continue? y/n" c

case $c in
  n|N)
    exit
    ;;
  y|Y)
    read -p "Please input a number: " n2
    n3=`echo $n2|sed 's/[0-9]//g'`
    if [ -n "$n3" ]
    then
	echo "Please input a number."
	exit
    fi
    if [ $n2 -le $n ]
    then
	echo "$n2 should grater than $n."
        exit
    fi
    for i in `seq $[$n+1] $n2`
    do
	echo $i
    done
    ;;
  *)
    echo "Please input y or n."
    ;;
esac

56 监控22端口是否被封

题目要求
一个同学不小心用iptables规则把sshd端口22给封掉了,结果不能远程登陆,要想解决这问题,还要去机房,登录真机去删除这规则。 问题来了,要写个监控脚本,监控iptables规则是否封掉了22端口,如果封掉了,给打开。 写好脚本,放到任务计划里,每分钟执行一次。

#!/bin/bash

iptables -nvL INPUT --line-numbers|grep -w 'dpt:22'|awk '$4 ~/REJECT|DROP/ {print $1}'
n=`wc -l /tmp/iptables.log`

if [ $n -gt 0 ]
then 
    for n in `tac /tmp/iptables.log`
    do
       iptables -D INPUT $n
   done
fi

1 * * * * bash /iptables_22.sh

57 给文件增加内容

题目要求
在文本文档1.txt第5行(假设文件行数大于5)后面增加如下内容:

# This is a test file.
# Test insert line into this file.
#!/bin/bash
#这个脚本用来给文件增加行
n=0
cat 1.txt |while read line
do
  n=$[$n+1]
  if [ $n -eq 5 ]
  then
      echo $line
     echo -e "# This is a test file.\n# Test insert line into this file."
  else 
      echo $line
  fi
done

58 备份etc目录

题目要求
设计一个shell程序,在每月第一天备份并压缩/etc目录的所有内容,存放在/root/bak目录里,且文件名为如下形式"yymmdd_etc.tar.gz",yy为年,mm为月,dd为日。

#!/bin/bash
d1=`date +%d`
d2=`date +%y%m%d`

if [ $d1 == "01" ]
then
    cd /etc
    tar -czf /root/bak/$d2_etc.tar.gz ./
fi

58 列出重复次数最多的10个单词

题目要求
将文件内所有的单词的重复次数计算出来,只需要列出重复次数最多的10个单词。

#!/bin/bash
#这个脚本用来找出重复的单词
for w in `sed 's/[^a-zA-Z]/ /g' $1`
do
    echo $w
done |sort |uniq -c|sort -nr|head

59 平均随机分组

题目要求
需求是,把所有的成员平均分成若干个小组。这里,提供一个人员列表,比如成员有50人,需要分成7个小组,要求随机性,每次和每次分组的结果应该不一致。
参考人员名单

xiaoguisheng
guoyuqing
xiongyongzheng
mengjintang
chaizuzhou
zhousheng
xufangming
zhaoliangyun
hanshiru
wangxianyi
zhangjipei
luxiuli
yangshugen
guoyongzhi
lijianguo
wuqiongchen
dinglin
yaoyashan
yinzijia
wangbencheng
liuxiuwen
chenzuqi
leyuguo
baozongyao
fenghao
sunxiaoquan
zhangyaxian
lijiuzhe
dulichun
lixi
shenpeiwen
zousilin
luoping
chaiyan
fandaozhang
huzixiang
jinzhen
zhujunfeng
liqianbiao
hangyanliang
luorenjian
loujianji
fujianzhou
gengyiwu
jinjigui
liuzhizhong
lisanyan
lisili
zhangyiyu
songguozhen
zhangxinghua
zhaozhiyong
huanghe
xiaojie
fanhongfei
wangguiwen
renshumin
songfuying
zhanghaibo
liguangqun
puaihua
yanzhihua
gaojixian
liulai
funing
chenruizhi
chendaxin
laishaoying
xujian
xiaozhekou
xuxiaping
jiangchunqing
#!/bin/bash
#这个脚本用来给人员分组


#人员列表文件
f=member.txt
#小组数
group_n=7
#人员总数
member_n=`wc -l $f|awk '{print $1}'`

#根据姓名计算该用户所在小组的id
get_n()
{
    #根据姓名计算cksum值
    l=`echo $1|cksum|awk '{print $1}'`
    #获取一个随机数
    n1=$RANDOM
    #cksum值和随机数相加,然后除以小组数取余,这样可以确保每次获取到的余数都不一样
    n2=$[$n1+$l]
    g_id=$[$n2%$group_n]
    #假如小组数为7,则余数范围0-6,如果余数为0,则小组为7
    if [ $g_id -eq 0 ]
    then
        g_id=$group_n
    fi
    echo $g_id
}

for i in `seq 1 $group_n`
do
    #n_$i.txt为临时文件,用来记录该小组内的成员
    #脚本之前执行过,则该文件会存在,本次执行脚本前应该删除掉这个临时文件
    [ -f n_$i.txt ] && rm -f n_$i.txt
done


shuf $f|while read name
do
    #计算用户所在小组的id
    g=`get_n $name`
    #将人员追加写入到他对应的小组里
    echo $name >> n_$g.txt
done

#定义计算文件行数的函数
nu(){
    wc -l $1|awk '{print $1}'
}

#获取组员人数最多的小组
max(){
    ma=0
    for i in `seq 1 $group_n|shuf`
    do
        n=`nu n_$i.txt`
        if [ $n -gt $ma ]
        then
            ma=$n
       fi
    done
    echo $ma
}

#获取组员人数最少的小组
min(){
    mi=$member_n
    for i in `seq 1 $group_n|shuf`
    do
       n=`nu n_$i.txt`
       if [ $n -lt $mi ]
       then
           mi=$n
       fi
    done
    echo $mi
}

#定义四舍五入函数
div()
{
    n=`echo "scale=1;$1/$2"|bc`
    n1=`echo "scale=1;$n+0.5"|bc`
    echo $n1|cut -d. -f1
}

#小组组员平均值(非四舍五入)
ava_n=$[$member_n/$group_n]
#小组组员平均值(四舍五入)
ava_n1=`div $member_n $group_n`

if [ $ava_n -eq $ava_n1 ]
then
    #定义初始最小值
    ini_min=1
    #以下while循环要做的事情,就是要把人数多的组里的人搞到人数少的组里去
    #此while循环的条件是,当人数最少的组成员数小于组员平均值
    while [ $ini_min -lt $ava_n1 ]
    do
        #找出人数最多的组
        m1=`max`
        #找出人数最少的组
        m2=`min`
        for i in `seq 1 $group_n|shuf`
        do
            n=`nu n_$i.txt`
            #找到人数最多的组对应的文件f1(可能有多个,这里取出现的第一个即可)
            if [ $n -eq $m1 ]
            then
                f1=n_$i.txt
            #找到人数最少的组对应的文件f2(可能有多个,这里取出现的第一个即可)
            elif [ $n -eq $m2 ]
            then
                f2=n_$i.txt
            fi
        done
        #取f1中最后一个人名
        name=`tail -n1 $f1`
        #将这个人名追加写入f2中
        echo $name >> $f2
        #在f1中删除刚刚取走的人名
        sed -i "/$name/d" $f1
        #把此时的最少组人员数赋值给ini_min
        ini_min=`min`
    done
else
    #定义初始最大值
    ini_max=$member_n
    while [ $ini_max -gt $ava_n1 ]
    do
        #找出人数最多的组
        m1=`max`
        #找出人数最少的组
        m2=`min`
        for i in `seq 1 $group_n|shuf`
        do
            n=`nu n_$i.txt`
            #找到人数最多的组对应的文件f1(可能有多个,这里取出现的第一个即可)
            if [ $n -eq $m1 ]
            then
                f1=n_$i.txt
                #找到人数最少的组对应的文件f2(可能有多个,这里取出现的第一个即可)
            elif [ $n -eq $m2 ]
            then
                f2=n_$i.txt
            fi
        done
        #取f1中最后一个人名
        name=`tail -n1 $f1`
        #将这个人名追加写入f2中
        echo $name >> $f2
        #在f1中删除刚刚取走的人名
        sed -i "/$name/d" $f1
        #把此时的最少组人员数赋值给ini_min
        ini_max=`max`
    done
fi

for i in `seq 1 $group_n`
do
    echo -e "\033[34m$i 组成员有:\033[0m"
    cat n_$i.txt
    #把临时文件删除
    rm -f n_$i.txt
    echo
done
}

60 比较数字大小

需求:
写一个shell脚本,比较两个数的大小,支持浮点数,两个数通过shell参数的形式提供。

#!/bin/bash
#这两个脚本用来比较两个数大小

if [ $# -ne 2 ]
then
    echo "请提供两个参数"
    exit
fi

if_number()
{
    if echo $1|grep -q '^-'
    then
        nu=`echo $1|sed 's/^-//'`
    else
        nu=$1
    fi
    n=`echo $nu|sed 's/[0-9.]//g'`
    if [ -n "$n" ]
    then
        echo "$1不是合法数字"
        exit
    fi
}
if_number $1
if_number $2

n1=`echo "$1>$2"|bc`
if [ $n1 -eq 1 ]
then
    echo "$1 > $2"
else
    if [ "$1" == "$2" ]
    then
        echo "$1 = $2"
    else
        echo "$1 < $2"
    fi
fi     

61 找出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

62 找出a,b文件差异行

题目要求
把当前用户下所有进程名字中含有"java"的进程关闭。

#!/bin/bash
#这个脚本用来杀进程
ps -u $USER|awk '$NF ~/java/ {print $1}' |xargs kill

63 并发备份mysql表

题目要求
用shell实现,以并发进程的形式将mysql数据库所有的表备份到当前目录,并把所有的表压缩到一个压缩包文件里。
假设数据库名字为mydb,用户名为aming,密码为passwd。

#!/bin/bash
#这个脚本用来并发备份数据库


N=5
mysql -uaming -ppasswd mydb -e "show tables"|sed '1d' > /tmp/table.txt
n=`wc -l /tmp/table.txt|awk '{print $1}'

div()
{
    n=`echo "scale=1;$1/$2"|bc`
    n1=`echo "scale=1;$n+0.5"|bc`
    echo $n1|cut -d. -f1
}

n1=`div $n $N`

split -l $n1 /tmp/table.txt 

myd()
{
    for t in `cat $1`
    do
    	mysqldump -uaming -ppasswd mydb $t > $t.sql
    done
}

for f in xaa xab xac xad xae
do
    myd $f &
done

wait 
tar czf mydb.tar.gz *.sql
rm -f *.sql 

64 并发备份mysql表

题目要求
一个网站,使用了cdn,全国各地有几十个节点。需要你写一个shell脚本来监控各个节点是否正常。 假如
1,监控的url为www.nsthink.com/index.php
2,源站ip为88.88.88.88。

#!/bin/bash
#这个脚本用来监控CDN节点 

s_ip=88.88.88.88
url=www.aminglinux.com/index.php
ipf=/data/ip.list

curl -x$s_ip:80 $url1 2>/dev/null >/tmp/$ip.txt
for ip in `cat $ipf`
do
  curl -x$ip:80 $url 2>/dev/null >/tmp/$ip.txt
  diff /tmp/source.txt /tmp/$ip.txt > /tmp/$ip.diff
  
  n=`wc -l /tmp/$ip.diff|awk '{print $1}'`
  if [ $n -gt 0 ]
  then
      echo "节点$ip有异常."
  fi  
done 

65 破解字符串

题目要求
已知下面的字符串是通过RANDOM随机数变量md5sum|cut -c 1-8截取后的结果,请破解这些字符串对应的md5sum前的RANDOM对应数字? 21029299 00205d1c a3da1677 1f6d12dd 890684ba

参考答案:

#!/bin/bash
#这个脚本用来破解字符串

for i in `seq 0 32767`
do
    m=`echo $i|md5sum|cut -c 1-8 `
    echo $i $m
done >/tmp/md5.txt

cat > c.txt <<EOF
21029299 
00205d1c 
a3da1677 
1f6d12dd 
890684ba
EOF

grep -f c.txt /tmp/md5.txt

66 判断CPU厂商

题目要求
写一个脚本:
判断当前主机的CPU生产商,其信息在/proc/cpuinfo文件中vendor id一行中。
如果其生产商为AuthenticAMD,就显示其为AMD公司;
如果其生产商为GenuineIntel,就显示其为Intel公司;
否则,就说其为非主流公司。

#!/bin/bash
#这个脚本用来判断CPU厂商

cpu=`grep '^vender_id' /proc/cpuinfo |head -1|awk -F ': ' '{print $2}'`
#if [ $cpu == "AuthenticAMD" ]
#then
#   echo "CPU厂商是Intel."
#else
#    echo "CPU厂商是非主流厂商。"
#fi
case $cpu in
  AuthenticAMD)
        echo "CPU厂商是AMD."
  ;;
  GenuineIntel)
        echo "CPU厂商是Intel."
  ;;
  *)  
        echo "CPU厂商是非主流厂商."
  ;;
esac

67 监控CPU使用率

题目要求
用shell写一个监控服务器cpu使用率的监控脚本。

参考答案:

#!/bin/bash
#用来计算CPU使用率

mail_user=906288036@qq.com

m_mail() {
    log=$1
    t_s=`date +%s`
    t_s2=`date -d "1 hours ago" +%s`
    if [ ! -f /tmp/$log ]
    then 
        #创建$log文件
        touch /tmp/$log
        #增加a权限,只允许追加内容,不允许更改或删除
        chattr +a /tmp/$log
        #第一次告警,可直接写入1小时以前的时间戳
        echo $t_s2 >> /tmp/$log
     fi
     #无论$log文件是否是刚刚创建,都需要查看最后一行的时间戳
     t_s2=`tail -l /tmp/$log|awk '{print $1}'`
     #取出最后一行即上次告警的时间戳后,理机写入当前的时间戳
     echo $t_s>>/tmp/$log
     #取两次时间戳差值
     
}

68 打印子进程

题目要求:
给出一个进程PID,打印出该进程下面的子进程以及子进程下面的所有子进程(只需要考虑子进程的子进程,再往深层次则不考虑)
核心要点:
ps -elf结果中PPID那一列为父进程PID, pstree -p 2 命令也可以查看,软件包psmisc,此处我们用ps -elf 编辑脚本

[root@docker:~]# cat printid.sh 
#/bin/bash
#这个脚本用来打印子进程

ps -elf > /tmp/pid.txt
read -p "Please input a pid: " p

if [ -z "$p" ]
then
    echo "你没有输入任何pid,请输入一个pid:"
    exit
fi
 
if ! grep -qw "$p" /tmp/pid.txt
then
    echo "你输入的pid不存在"
    exit
fi

get_children_pid()
{
    p1=$1
    ps -elf |awk -v p2=$p1 '$5==p2 {print $4}' > /tmp/$p1.txt
    n=`wc -l /tmp/$p1.txt|awk '{print $1}'`
    if [ $n -eq 0 ]
    then
        echo "进程$1下没有子进程"
    else
        echo "进程$1下的子进程是:"
        cat /tmp/$p1.txt
    fi
}

get_children_pid $p

#下面代码获取子进程的子进程
for c_p in `cat /tmp/$p.txt`
do
    get_children_pid $c_p
done

69 lamp增加项目

需求背景:
服务器上,跑的lamp环境,上面有很多客户的项目,每个项目就是一个网站,由于客户在不断增加,每次增加客户,就需要配置相应mysql,ftp,以及httpd,这种工作具有很强的重复性,所以用脚本实现非常合适。mysql增加的是对应客户项目的数据库,用户密码,ftp增加的是对应项目的用户,密码(使用vsftpd,虚拟用户模式),httpd就是要增加虚拟主机配置段。
核心要点:
清楚增加mysql库和用户,增加httpd虚拟主机,增加ftp用户的命令,只要能在命令行搞定,那么在脚本中就没问题。

#!/bin/bash
#本脚本功能是在LAMP环境中增加站点,包括apache配置,ftp增加用户,mysql增加库和用户

#网站目录
webdir=/data/wwwroot

#ftp的虚拟用户配置文件目录
ftpuserfile=/root/login

#mysql命令行登陆root
mysqlc="/usr/local/mysql/bin/mysql -uroot -p123456"

#apache虚拟主机配置文件
httpd_config_f="/usr/local/apache2/conf/extra/httpd-vhosts.conf"

#定义增加mysql库和用户的函数
add_mysql_user()
{
    #生成随机密码
    mysql_p=`mkpasswd -s 0 -1 12`

    #将密码保存到临时文件里,这里的$pro为用户自定义的项目名字
    echo "$pro $mysql_p" >/tmp/$pro.txt

    #这里使用嵌入文档的形式(需顶格),将创建用户并授权的命令传递给mysql
$mysqlc <<EOF
create database $pro;
grant all on $pro.* to "$pro"@'127.0.0.1' identified by "$mysql_p";
#下面的EOF必须顶格
EOF
}

#定义增加FTP用户的函数
add_ftp_user()
{
    ftp_p=`mkpasswd -s 0 -1 12`
    echo "$pro" >> $ftpuserfile
    echo "$ftp_p" >> $ftpuserfile

    #将用户、密码文件转换为密码db文件
    db_load -T -t hash -f $ftpuserfile /etc/vsftpd/vsftpd_login.db
    cd $ftpudir
  
    #这里的aaa是一个文件,是一个测试项目,可以作为配置模板
    cp aaa $pro
 
    #把里面的aaa改为新的项目名字
    sed -i "s/aaa/$pro/" $pro

    #重启vsftpd服务
    /etc/init.d/vsftpd restart
}

#定义增加apache虚拟主机的函数
config_httpd()
{
    #增加网站根目录,和域名保持一致,这里的$dom为用户自定义的域名
    mkdir $webdir/$dom
   
    #将网站根目录属主属组设置为ftp用户
    chown vsftpd:vsftpd $webdir/$dom
  
    #用嵌入文档(需顶格),把虚拟主机配置写入到配置文件里
cat >> $httpd_config_f <<EOF
<VirtualHost *:80>
    DocumentRoot $webdir/$dom
    ServerName $dom
    <Directory $webdir/$dom>
        AllowOverride none
        Require all granted
    </Directory>
</VirtualHost>
EOF
   
     #重载apache服务
     /usr/local/apache2/bin/apachectl graceful
}

read -p "input the project name: " pro
read -p "input the domain: " dom


add_mysql_user
add_ftp_user
config_httpd

71 判断文件存在并计算单词个数

题目要求:
判断所给目录内哪些二级目录下有没有text.txt文件。
有text.txt文件的二级目录,计算出该test.txt文件里面所给出单词的次数。
假如脚本名字为1.sh, 运行脚本的格式为 ./1.sh 123 root,其中123为目录名字,而root为要计算数量的单词。
参考答案

#!/bin/bash
#这个脚本用来判断文件是否存在并计算单词个数

if [ $# -ne 2 ]
then
    echo "请提供两个参数,第一个参数是目录名字,第二个参数是单词"
    exit
fi

cd $1
for f in `ls .`
do
    if [ -d $f ]
    then
	if [ -f $f/test.txt ]
	then
	    n=`grep -cw "$2" $f/test.txt`
	    echo  "$1/$f目录下面有test.txt, 该test.txt里面的有$n个$2."
	fi
    fi
done

71 简易计算器

题目要求:
用shell写一个简易计算器,可以实现加、减、乘、除运算,假如脚本名字为1.sh,执行示例:./1.sh 1 + 2

核心要点:
case in esac结构

#!/bin/bash
#这个脚本用来实现简易计算器

if [ $# -ne 3 ]
then 
    echo "你给的参数个数不对,应该3个参数"
    exit
fi

if_number()
{
   n1=`echo $1|sed 's/[0-9.]//g'`
   if [ -n "$n1" ]
   then
       echo "$1不是数字."
       exit
   fi
}


if_number $1
if_number $3

case $2 in 
   +)
     echo "$1+$3"|bc
     ;;
   -)
     echo "$1-$3"|bc
     ;;
   \*)
     echo "$1*$3"|bc
     ;;
   /)
     echo "scale=2;$1/$3"|bc
     ;;
   *)
     echo "你给出的格式不对,第二个参数只能是+,-,*,/"
     ;;
esac

72 打印正方形

题目要求:
交互式脚本,根据提示,需要用户输入一个数字作为参数,最终打印出一个正方形。在这里我提供一个linux下面的特殊字符■,可以直接打印出来。

示例: 如果用户输入数字为5,则最终显示的效果为

■ ■ ■ ■ ■
■ ■ ■ ■ ■
■ ■ ■ ■ ■
■ ■ ■ ■ ■
■ ■ ■ ■ ■

参考答案:

#!/bin/bash
#这个脚本用来打印正方形

while :
do
    read -p "Please input a nuber: " n
    n1=`echo $n|sed 's/[0-9]//g'` 
    if [ -n "$n1" ]  
    then
        echo "$n is not a nuber." 
        continue
    else
	break
    fi
done
#以上判断输入的是否是纯数字,不是纯数字循环输入直到输入的为纯数字,如果输入的是纯数字,则退出


for i in `seq 1 $n`
do
    for j in `seq 1 $n`
    do
	echo -n "■ " //-n不换行输出      
    done
    echo 
done

#以上为一个循环里面嵌套一个循环,打印$n行$n列

73 问候用户

题目要求:
写一个脚本,依次向/etc/passwd中的每个用户问好,并且说出对方的ID是什么,如:
Hello, root,your UID is 0.

[root@aliyun:~/server/scripts/shell100]# cat 74.sh 
#!/bin/bash
#这个脚本用来问候用户

cat /etc/passwd |while read line
do
    username=`echo $line|awk -F ':' '{print $1}'`
    uid=`echo $line|awk -F ':' '{print $3}'`
    echo "hello,$username,your uid is $uid"
done

74 格式化输出xml

题目要求
linux系统 /home目录下有一个文件test.xml,内容如下:

<configuration>
    <artifactItems>
        <artifactItem>
       <groupId>zzz</groupId>
       <artifactId>aaa</artifactId>
    </artifactItem>
    <artifactItem>
       <groupId>xxx</groupId>
       <artifactId>yyy</artifactId>
    </artifactItem>
    <!-- </artifactItem><groupId>some groupId</groupId>
       <version>1.0.1.2.333.555</version> </artifactItem>-->
    </artifactItems>
</configuration>

请写出shell脚本删除文件中的注释部分内容,获取文件中所有artifactItem的内容,并用如下格式逐行输出: artifactItem:groupId:artifactId:aaa

#!/bin/bash
#这个脚本用来格式化xml文件

sed '/<!--.*-->/d' test.xml > test2.xml
egrep -n '<!--|\-\->' test2.xml |awk -F ':' '{print $1}' > /tmp/line_number1.txt
n=`wc -l /tmp/line_number1.txt|awk '{print $1}'`
n1=$[$n/2]
for i in `seq 1 $n1`
do
    j=$[$i*2]
    k=$[$j-1]
    x=`sed -n "$k"p /tmp/line_number1.txt`
    y=`sed -n "$j"p /tmp/line_number1.txt`
    sed -i "$x,$y"d test2.xml
done

grep -n 'artifactItem>' test2.xml |awk '{print $1}' |sed 's/://' > /tmp/line_number2.txt
n2=`wc -l /tmp/line_number2.txt|awk '{print $1}'`

get_value(){
    sed -n "$1,$2"p test2.xml|awk -F '<' '{print $2}'|awk -F '>' '{print $1,$2}' > /tmp/value.txt
    
    cat /tmp/value.txt|while read line
    do
        x=`echo $line|awk '{print $1}'`
        y=`echo $line|awk '{print $2}'`
        echo artifactItem:$x:$y
    done
}

n3=$[$n2/2]
for j in `seq 1 $n3`
do
    m1=$[$j*2-1]
    m2=$[$j*2]
    nu1=`sed -n "$m1"p /tmp/line_number2.txt`
    nu2=`sed -n "$m2"p /tmp/line_number2.txt`
    nu3=$[$nu1+1]
    nu4=$[$nu2-1]
    get_value $nu3 $nu4
done

75 小函数

题目要求:

请撰写一个shell函数,函数名为 f_judge,实现以下功能

当/home/log目录存在时将/home目录下所有tmp开头的文件或目录移到/home/log目录。

当/home/log目录不存在时,创建该目录,然后退出。

#!/bin/bash
#这个脚本用来写一个小函数

f_judge()
{ 
    if [ -d /home/log ]
    then 
        #find /home -name "tmp*" |xargs -i mv {} /home/log/
        find /home -name "tmp*" -exec mv {} /home/log/ \;
    else
        mkdir /home/log
        exit
    fi
}

f_judge

76 批量杀进程

题目要求:
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进程

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-ped.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

77 打包日志文件备份

题目要求:
写一个脚本查找/data/log目录下,创建时间是3天前,后缀是*.log的文件,打包后发送至192.168.1.2服务上的/data/log下,并删除原始.log文件,仅保留打包后的文件。

#!/bin/bash
#这个脚本用来查找老日志文件打包

cd /data/log
find . -type f -name "*.log" -mtime +3 > /tmp/old_log
d=`date +%F`

tar czf $d.tar.gz `cat /tmp/old_log|xargs`
rsync -a $d.tar.gz 192.168.1.2:/data/log/
cat /tmp/old_log|xargs rm

78 一行变两行

题目要求
有如下文本,其中前5行内容为

1111111:13443253456
2222222:13211222122
1111111:13643543544
3333333:12341243123
2222222:12123123123
用shell脚本处理后,按下面格式输出:

[1111111]
13443253456
13643543544
[2222222]
13211222122
12123123123
[3333333]
12341243123

#!/bin/bash

#此脚本用来处理文本

for w in `awk -F ':' {print $1}' |sort |uniq`
do
    echo "[$w]"
    awk -v w2=$w -F ':' '$1==w2 {print $2}' 3.txt
done

79 批量删除日志

要求:
两类机器一共300多台,写个脚本自动清理这两类机器里面的日志文件。在堡垒机批量发布,也要批量发布到crontab里面。
A类机器日志存放路径很统一,B类机器日志存放路径需要用匹配(因为这个目录里除了日之外,还有其他文件,不能删除。匹配的时候可以用.log)
A类:/opt/cloud/log/删除7天前的 B类:/opt/cloud/instances/删除15天前的
要求写在一个脚本里面。不用考虑堡垒机上的操作,只需要写出shell脚本。

#!/bin/bash
#这个脚本用来删除老日志

dir1=/opt/cloud/log/
dir2=/opt/cloud/instance/

if [ -d $dir1 ]
then
    find $dir1 -type f -mtime +7 |xargs rm
elif [ -d $dir2 ]
then
    find $dir2 -name "*.log" -type f -mtime +15 |xargs rm
fi

80 贷款计算器

题目要求
贷款有两种还款的方式:等额本金法和等额本息法,简单说明一下等额本息法与等额本金法的主要区别:

等额本息法的特点是:每月的还款额相同,在月供中“本金与利息”的分配比例中,前半段时期所还的利息比例大、本金比例小,还款期限过半后逐步转为本金比例大、利息比例小。所支出的总利息比等额本金法多,而且贷款期限越长,利息相差越大。 等额本金法的特点是:每月的还款额不同,它是将贷款额按还款的总月数均分(等额本金),再加上上期剩余本金的月利息,形成一个月还款额,所以等额本金法第一个月的还款额最多 ,而后逐月减少,越还越少。所支出的总利息比等额本息法少。

两种还款方式的比较不是我们今天的讨论范围,我们的任务就是做一个贷款计算器。其中: 等额本息每月还款额的计算公式是: 每月还款额=[贷款本金×月利率×(1+月利率)还款月数]÷[(1+月利率)还款月数-1] 等额本金每月还款额的计算公式是: 每月还款额=贷款本金÷贷款期数+(本金-已归还本金累计额)×月利率

参考答案

#!/bin/bash
#这个脚本用来实现简易的房贷计算器

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

81 监控磁盘 报错

题目要求
阿里云的机器,今天收到客服来的电话,说服务器的磁盘io很重。于是登录到服务器查看,并没有发现问题,所以怀疑是间歇性地。正要考虑写个脚本的时候,幸运的抓到了一个线索,造成磁盘io很高的幕后黑手是mysql。此时去show processlist,但未发现有问题的队列。原来只是一瞬间。只好继续来写脚本,思路是,每5s检测一次磁盘io,当发现问题去查询mysql的processlist。

参考答案
核心要点:iostat -xd 1 5,主要看%util,%util:一秒中有百分之多少的时间用于 I/O 操作,或者说一秒中有多少时间 I/O 队列是非空的。即 delta(use)/s/1000 (因为use的单位为毫秒),如果 %util 接近 100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈。

#!/bin/bash
#这个脚本用来监控磁盘IO

if ! while iostat &>/dev/null //如果iostat命令不正常运行
then
    yum install -y sysstat
fi
#以上用来检查iostat命令是否安装

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` //求出每5秒的%util和
    a=`echo "scale=2;$sum/5"|bc` //scale=2保留2位小数,求平均值
    b=`echo $a|cut -d . -f 1` //取整数
    if [ $b -gt 90 ]
    then
        mysql -uroot -pxxx -e "show processlist" >mysql_$t.log
    fi
    sleep 1
done

82 查看tomcat日志-生产实例

题目要求
写一个截取tomcat catalina.out日志的脚本 tomcat实例t1-t4

# find  /opt/TOM/   -name  catalina.out
/opt/TOM/t1/logs/catalina.out
/opt/TOM/t3/logs/catalina.out
/opt/TOM/t4/logs/catalina.out
/opt/TOM/t2/logs/catalina.out
**要求:**

这个脚本可以取tomcat实例t1-t4的日志
这个脚本可以自定义取日志的起始点 ,比如取今天早上10点之后到现在的数据
这个脚本可以自定义取日志的起始点和终点,比如取今天早上9点到晚上8点的数据 catalina.out 日志内容
Oct 29, 2018 01:52:24 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Oct 29, 2018 01:52:24 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Oct 29, 2018 01:52:24 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 2102 ms
#!/bin/bash
#这个脚本用来查看tomcat日志

LANG=en
logfile="/opt/TOM/$1/logs/catalina.out"

#根据日志时间格式,将当天的英文月、数字日期、数字年作为变量赋值给d_mdy
d_mdy=`date "+%b %d, %Y"`

#判断参数个数
if [ $# -ne 2 ] && [ $# -ne 3 ]
then
    echo "你提供的参数个数不对,请提供2个或者3个参数。例:sh $0 t1 08:01:00 14:00:00" 
    exit 1
fi

#判断第一个参数是否符合要求
if ! echo $1|grep -qE '^t1$|^t2$|^t3$|^t4$'
then
    echo "第一个参数必须是t1、t2、t3或t4"
    exit 1
fi 

#判断时间有效性
judge_time()
{
    date -d "$1" +%s &>/dev/null
    if [ $? -ne 0 ]
    then
        echo "你提供的时间$1格式不正确"
        exit 1
    fi
}

#将24小时制时间转换为12小时
tr_24_12()
{
    date -d "$1" +%r
}

#判断提供的时间点是否在日志中出现
judge_time_in_log()
{
    if ! grep -q "$d_mdy $(tr_24_12 $1)" $logfile
        then
            echo "你提供的时间$1在日志$logfile中不曾出现,请换一个时间点"
            exit 1
        fi    
}

#判断第2个参数是否合法
judge_time $2

#判断起始时间点是否出现在日志里
judge_time_in_log $2

#如果提供第3个参数
if [ $# -eq 3 ]
then
    #判断第3个参数是否合法
    judge_time $3

    #判断起始时间是否早于结束时间
    t1=`date -d "$2" +%s`
        t2=`date -d "$3" +%s`
        if [ $t2 -lt $t1 ]
        then
            echo "你提供的时间$2比$3要晚,应该把早的时间放到前面"
            exit
        fi

        #判断提供的结束时间点是否出现在日志中
        judge_time_in_log $3
fi


#取起始时间所在行行号
begin_n=`grep -n "$d_mdy $(tr_24_12 $2)" $logfile|head -1|awk -F ':' '{print $1}'`

#取结束时间所在行行号,并用sed截取日志内容
if [ $# -eq 3 ]
then
    n=`grep -n "$d_mdy $(tr_24_12 $3)" $logfile|tail -1|awk -F ':' '{print $1}'`
    #结束日期所在行的下一行才是日志的内容
    end_n=$[$n+1]
    sed -n "$begin_n,$end_n"p $logfile
else
    sed -n "$begin_n,$"p $logfile
fi

83 打印城市名字

题目要求
写一个脚本让用户输入多个城市的名字(可以是中文),要求不少于5个,然后把这些城市存到一个数组里,最后用for循环把它们打印出来。

参考答案

#!/bin/bash
#这个脚本用来打印城市名字


read -p "输入不少于5个城市的名字,用空格分隔开。" name

n=`echo $name|awk '{print NF}'`

if [ $n -lt 5 ]
then
    echo "请输入至少5个城市的名字."
    exit
fi

city=($name)

for i in `seq 0 $[${#city[@]}-1]`
do
    echo ${city[$i]}
done

84 代码上线

题目要求
需求背景是: 一个业务,有3台服务器(A,B,C)做负载均衡,由于规模太小目前并未使用专业的自动化运维工具。有新的需求时,开发同事改完代码会把变更上传到其中一台服务器A上。但是其他2台服务器也需要做相同变更。

写一个shell脚本,把A服务器上的变更代码同步到B和C上。 其中,你需要考虑到不需要同步的目录(假如有tmp、upload、logs、caches)

参考答案
核心要点:rsync同步,密钥认证或者expect脚本

#这个脚本用来代码上线

dir=/data/wwwroot/www.aaa.com
B_IP=1.1.1.1
C_IP=2.2.2.2

rs()
{
    rsync -azP --exclude="logs" --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

85 统计网站并发量

题目要求
需求背景:

需要统计网站的并发量,并绘图。 说明: 只需要写出shell脚本即可,不用关心zabbix配置。

假设日志路径 /data/logs/www.aaa.com_access.log

日志格式如下:

112.107.15.12 - [07/Nov/2018:09:59:01 +0800] www.aaa.com "/api/live.php" 200"-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)"

参考答案
核心要点:查看日志中1秒内的日志数量,即并发数(每秒请求多少次)

#!/bin/bash
LANG=en
t=`date -d "-1 second" +%d/%b/%Y:%T`
log=/data/logs/www.aaa.com_access.log

tail -1000 $log |grep -c "$t" 

86 关闭不常用的的自启动服务

题目要求
在centos6系统里,我们可以使用ntsysv关闭不需要开机启动的服务,当然也可以使用chkconfig工具来实现。

写一个shell脚本,用chkconfig工具把不常用的服务关闭。脚本需要写成交互式的,需要我们给它提供关闭的服务名字。

参考解析;
核心要点:chkconfig --list

#!/bin/bash
#这个脚本用来关闭服务

LANG=en

while :
do
    chkconfig --list 2>/dev/null|grep '3:on' |awk '{print $1}' >/tmp/on_ser.txt
    echo -e "\033[32m系统里开启了这些服务: \033[0m"
    cat /tmp/on_ser.txt
    echo 
    read -p "Please select a service from this list: " s
 
    if ! grep -qw "$s" /tmp/on_ser.txt
    then 
        echo -e "\033[31m你提供的服务名并未开启.\033[0m"
        continue
    fi 
    chkconfig $s off
    break
done

87 彻底关闭tomcat服务

题目要求
在生产环境中,经常遇到tomcat无法彻底关闭,也就是说用tomcat自带shutdown.sh脚本无法将java进程完全关掉。所以,需要借助shell脚本,将进程杀死,然后再启动。

写一个shell脚本,实现上述功能。彻底杀死一个进程的命令是 kill -9 pid。

#!/bin/bash      
#这个脚本用来彻底杀死Tomcat进程

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

88 批量去掉.bak后缀名

题目要求
至少用两种方法,批量把当前目录下面所有文件名后缀为.bak的后缀去掉,比如1.txt.bak去掉后为1.txt

参考答案

#!/bin/bash
#这个脚本用来去掉文件名后缀

for f in `ls -d ./*.bak` //-d选项,将目录像文件一样显示,而不是仅仅显示其下的文件
do
#   mv $f `echo $f|sed 's/.bak$//'`
    f1=`echo $f |awk -F '.bak$' '{print $1}'`
    mv $f $f1
done

89 查询域名是否过期

题目要求
写一个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

90 自动配置密钥认证

题目要求
写一个shell脚本,当我们执行时,提示要输入对方的ip和root密码,然后可以自动把本机的公钥增加到对方机器上,从而实现密钥认证。

参考答案

核心要点:expect ssh-copy-id

#!/bin/bash
#这个脚本用来自动配置密钥认证

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

91 部署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"

92 管理docker

题目要求
写一个脚本,实现一键管理docker容器,比如启动、关闭、删除容器等操作。 要求:

脚本支持启动全部容器、关闭全部容器、删除全部容器
需要提示用户如何使用该脚本,需给出范例
参考答案

#!/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

93 安装配置samba服务

题目要求
写个shell脚本,能够实现一键安装并配置samba服务,执行该脚本时需要带一个参数,为共享的目录,目录若不存在,需自动创建。

要求,任何人都可以访问,且不用密码,并且目录是只读的。

参考答案

#!/bin/bash
#这个脚本用来一键安装并配置samba

#判断参数是否为1
if [ "$#" -ne 1 ]  
then
    echo "运行脚本的格式为:$0 /dir/"  //$0为脚本的名字
    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

94 批量查看堕胎机器负载

题目要求
假如公司的一个业务,域名为www.nsthink.com,现在有5台机器在跑。为了快速查看这5台机器的负载,需要你写一个Shell脚本,运行脚本后,就能一下子把5台机器的负载全部打印出来。

参考答案

#!/bin/bash
#这个脚本用来批量查机器负载

for ip in `cat /tmp/ip.list`
do
    echo $ip 
    ssh $ip "uptime"
done

95 自动挂载云盘

题目要求
我们使用的云主机,购买一块云盘后,默认并不是挂载状态的,用shell写一个脚本,只要把盘符和挂载点以参数的形式提供给脚本,该脚本就可以自动格式化、挂载。

要求:

不用分区,直接格式化
格式化为ext4文件系统类型

参考解析
核心要点:
1,参数要有两个,第一个是设备名,第二个是挂载点,需要判断给出的参数个数以及是否可用
2,格式化磁盘的命令为mkfs.ext4磁盘设备名
3,mount命令挂载,挂载完后还需要编辑/etc/fstab配置文件

#!/bin/bash
#这个脚本用来自动挂载磁盘

#判断参数是否是2个
if [ $# -ne 2 ]  
then
    echo "Usage $0 盘符 挂载点, 如: $0 /dev/xvdb /data"
    exit 1
fi

#判断是否是一个设备文件
if [ ! -b $1 ]
then
    echo "你提供的盘符不正确,请检查后再操作"
    exit 1
fi

#格式化磁盘
echo "格式化$1"
mkfs -t ext4 $1

#判断参数2是否存在
if [ ! -d $2 ] ;then
        mkdir -p $2
fi

#判断/etc/fatab中是否有这个挂载点
n=`awk '$NF == "$2"' /etc/fstab|wc -l`

#如果不存在这个挂载点,则将挂载信息写到/etc/fstab中去,并使用命令mount -a实现自动挂载
if [ $n -eq 0 ]
then
    echo "$1              $2                      ext4   defaults  0  0" >> /etc/fstab
    mount -a
else
    echo "配置文件/etc/fstab中已经存在挂载点$2,请检查一下."
    exit 1
fi

96 并发备份数据库

题目要求

需求背景:

领导要求小明备份数据库服务器里面的100个库(数据量在几十到几百G),需要以最快的时间完成(5小时内),并且不能影响服务器性能。

参考解析:
通过命名管道FIFO来实现


#!/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  #将后续的命令产生的记录都记录到日志中,方便排错

#判断xtrabackup安装工具
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

#绑定1000文件描述符
exec 1000<>$fifofile

#定义并发量为10
thread=10
#循环10次,写入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

97 打印三角形

题目要求
之前咱们打印过正方形,也打印过乘法口诀,那今天来打印一个三角形(正三角形,元素用*表示)。

参考答案

#!/bin/bash
#这个脚本用来打印三角形

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

for i in `seq 1 $n`
do
    j=$[$n-$i]
    for m in `seq $j`
    do
        echo -n " "
    done

    for p in `seq 1 $i`
    do
        echo -n "* "
    done
    echo
done

98 截取字符串

题目要求
利用你学过的知识点,想办法根据要求截取出字符。

字符串var=http://www.aaa.com/root/123.htm

1.取出www.aaa.com/root/123.htm

2.取出123.htm

3.取出http://www.aaa.com/root

4.取出http:

5.取出http://

6.取出root/123.htm

7.取出123

参考答案

#!/bin/bash
#这个脚本用来截取字符串

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'

99 修改文本格式

题目要求
请把下面的字符串:

zhangsan
y97JbzPru
lisi
5JhvCls6q
xiaowang
Nnr8qt2Ma
laoma
iqMtvC02y
zhaosi
9fxrb4sJD
改为如下:

zhangsan:y97JbzPru
lisi:5JhvCls6q
xiaowang:Nnr8qt2Ma
laoma:iqMtvC02y
zhaosi:9fxrb4sJD
参考答案

#!bin/bash
#这个脚本用来格式化文本
#作者:猿课-阿铭 www.apelearn.com
#日期:2018-12-19

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

100 执行rm前先备份脚本

题目要求
linux系统的rm命令太危险,一不小心就会删除掉系统文件。 写一个shell脚本来替换系统的rm命令,要求当删除一个文件或者目录时,都要做一个备份,然后再删除。下面分两种情况,做练习:

简单
假设有一个大的分区/data/,每次删除文件或者目录之前,都要先在/data/下面创建一个隐藏目录,以日期/时间命名,比如/data/.201703271012/,然后把所有删除的文件同步到该目录下面,可以使用rsync -R 把文件路径一起同步

复杂
不知道哪个分区有剩余空间,在删除之前先计算要删除的文件或者目录大小,然后对比系统的磁盘空间,如果够则按照上面的规则创建隐藏目录,并备份,如果没有足够空间,要提醒用户没有足够 的空间备份并提示是否放弃备份,如果用户选择y,则直接删除文件或者目录,如果选择n,则提示未删除,然后退出脚本。

参考答案
简单脚本:

1. 简单
#!/bin/bash
#这个脚本用来自定义rm


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."
#停顿5秒,每秒打印一个.
        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
posted @ 2020-06-17 10:59  michaelni  阅读(636)  评论(0编辑  收藏  举报