欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

几个例子掌握shell脚本

程序员文章站 2022-05-29 23:23:33
...

    传说一个俄罗斯程序员写了一堆脚本,会给老婆发加班短信、会在宿醉不醒时给自己请假、会自动根据邮件恢复客户的数据库、还可以一键远程煮咖啡,好神奇的样子(虽然我到现在也不知道哪里能买到能联网的咖啡机)。

    程序最大的意义就是代替人们做重复的工作。

    在我们日常的运维工作中,常常敲一些重复的命令,比如我们在调试程序时,每次都要重启应用,每次都要敲一堆重复命令,敲一次两次还可以,但是当到敲十几次、几十次就要抓狂了,这个时候一个脚本就很有必要了。再比如我们在服务器上部署一个java程序,由于内存溢出或内存泄漏等原因程序可能挂掉,所以我们就需要一个检查进程的脚本了,并且通过定时任务实现进程监控。

    下面我将列几个我之前写过的脚本:

  1. 应用启停脚本
    #!/bin/bash
    
    stopuwsgi()
    {
        echo "关闭uwsgi..."
        for pid in `ps -aux|grep uwsgi|grep ini|grep -v grep|awk '{print $2}'` ; do
            kill -9 ${pid}
        done
        sleep 3
        echo "已关闭!"
    }
    startuwsgi()
    {
        if [ `ps -aux|grep uwsgi|grep ini|grep -v grep|wc -l` -ne 0 ]; then
            echo 'ERR!进程已存在,未开启!'
            return
        fi
        echo '开启uwsgi...'
        cd /app/ds_website
        uwsgi --ini uwsgi.ini
        sleep 3
        echo "uwsgi进程数:`ps -aux|grep uwsgi|grep ini|grep -v grep|wc -l`"
        echo '已开启!'
    }
    restartuwsgi()
    {
        stopuwsgi
        startuwsgi
        echo '已重启!'
    }
    
    menu()
    {
        while [ "1" ]
        do
            clear
            echo "功能菜单:"
            echo "1、重启uwsgi"
            echo "2、启动uwsgi"
            echo "3、停止uwsgi"
            echo "0、退出"
            echo "select==>>"
            tput cup 5 10
            read choice
            case ${choice} in
            0) break;;
            1) echo "重启uwsgi(y/n)"
                read key
                if [ ${key} = 'y' -o ${key} = 'Y' ]; then
                    restartuwsgi
                fi
                echo '按任意键继续...'
                read key
                continue;;
            2) echo "启动uwsgi(y/n)"
                read key
                if [ ${key} = 'y' -o ${key} = 'Y' ]; then
                    startuwsgi
                fi
                echo '按任意键继续...'
                read key
                continue;;
            3) echo "停止uwsgi(y/n)"
                read key
                if [ ${key} = 'y' -o ${key} = 'Y' ]; then
                    stopuwsgi
                fi
                echo '按任意键继续...'
                read key
                continue;;
            esac
        done
    }
    
    if [ $1 = 'start' ]; then
        startuwsgi
    elif [ $1 = 'restart' ]; then
        restartuwsgi
    elif [ $1 = 'stop' ]; then
        stopuwsgi
    else
        menu
    fi
    
     说明:该脚本用于启停python Django 程序容器:uwsgi,由于uwsgi启动后会开启8个进程,如果手动重启需要ps找出所有进程的pid,然后逐个kill掉,然后再开启uwsgi,在调试程序时经常要启停应用,如果每次都逐一kill进程,简直是痛苦的一匹。

    该脚本支持菜单和参数两种方式使用,涉及常见的进程操作、菜单的实现、参数的实现,大家可以在写脚本时用作参考。

  2. 应用部署以及启停脚本
    #!/bin/bash
    
    setup(){
    	echo "部署spider"
    	cd /app
    	echo "删除原代码..."
    	rm -rf spider
    	echo "当前工作目录:`pwd`"
    	echo "开始clone项目..."
    	git clone https://gitee.com/dushen666/spider.git
    	cd spider
    	echo "当前工作目录:`pwd`"
    	echo "修改配置文件..."
    	sed -i "s/self.conn = MySQLdb.connect.*/self.conn = MySQLdb.connect(host='localhost', port=3306, user='root', passwd='pawd123--', db='movie-website1', charset='utf8')/" /app/spider/spider/pipelines.py
    	echo '应用部署完毕!'
    }
    
    startup(){
    	echo "开启spider"
    	cd ~/spider
    	nohup python /app/spider/quickstart.py &
    	echo "spider已开启,5秒后打开日志"
    	sleep 5
    	tail -f /app/spider/nohup.out
    }
    
    sstop(){
    	echo "结束spider"
    	if [ `ps -aux|grep python|grep quickstart|grep -v grep|wc -l` -eq '0' ]; then
    		echo "spider进程不存在,无需停止!"
    		return
    	fi
    	for pid in `ps -aux|grep python|grep quickstart|grep -v grep|awk '{print $2}'`;do
    		kill -9 $pid
    		echo "已杀死进程$pid"
    	done
    	if [ `ps -aux|grep tomcat|grep -v grep|wc -l` -eq '0' ]; then
    		echo "OK!成功停止服务!"
    		return
    	fi
    	echo "ERR!停止服务异常,需手动停止!"
    }
    
    ttest(){
    	if [ `ps -aux|grep python|grep quickstart|grep -v grep|wc -l` -eq '0' ]; then
    		echo "spider进程不存在,自动开启!"
    		cd ~/spider
    		nohup python /app/spider/quickstart.py &
    		echo "已开启spider"
    	fi
    }
    
    case $1 in
    	'setup' ) 
    		setup;;
    	'start' )
    		startup;;
    	'stop' )
    		sstop;;
    	'test' )
    		ttest;;
    	* )
    		echo "Usage:`basename $0` [options] filename";;
    esac
    
    
     说明:如何将开发好的代码快速部署到服务器上,最常见的部署代码方式就是将程序编译好的增量包FTP放到服务器上,然后解压覆盖,再重启服务。本脚本利用github实现了程序的快速部署,大致流程是这样:删掉服务器上原有的代码->使用git clone将github上的代码克隆到本地->修改配置文件->重启服务。本脚本只适合使用github或码云管理的项目,在想要发布程序时,只需要执行一下脚本,所有的事情就自动做了。

    该脚本涉及sed修改配置文件、git clone等知识,可用作参考。

  3. 应用巡检以及启停脚本
    #! /bin/bash
    
    ##################################################################
    ##			作者:	杜神				##
    ##			日期:	2018-01-19			##
    ##			功能:	DS管理工具集			##
    ##			version:	1.0			##
    ##################################################################
    
    
    APP_PORT1=8080
    APP_PORT2=8443
    REDIS_PORT1=6666
    REDIS_PASSWD='pawd123--'
    LOG_NAME=`date +%Y%m%d`.log
    LOG_PATH=/app/logs/script/
    TOMCAT_HOME=~/environment/tomcat8
    MAILS=/app/conf/mail.lst
    FLAG=/app/conf/err
    
    set -i
    source ~/.bashrc
    #应用服务状态检测
    app_check()
    {
    	echo "应用服务状态检测……"|tee -a $LOG_PATH$LOG_NAME
    	state='0' #0正常	1异常
    	if [ `ps -aux|grep tomcat|grep -v grep|wc -l` -eq '1' ]; then
    		echo "tomcat进程正常"|tee -a $LOG_PATH$LOG_NAME
    	else
    		echo "tomcat进程异常"|tee -a $LOG_PATH$LOG_NAME
    		state='1'
    	fi
    	
    	if [ `netstat -an|grep 0.0.0.0:$APP_PORT1|grep LISTEN|wc -l` -eq '1' ]; then
    		echo "端口$APP_PORT1状态正常"|tee -a $LOG_PATH$LOG_NAME
    	else
    		echo "端口$APP_PORT1异常"|tee -a $LOG_PATH$LOG_NAME
    		state='1'
    	fi
    
    	if [ `netstat -an|grep 0.0.0.0:$APP_PORT2|grep LISTEN|wc -l` -eq '1' ]; then
    		echo "端口$APP_PORT2状态正常"|tee -a $LOG_PATH$LOG_NAME
    	else
    		echo "端口$APP_PORT2异常"|tee -a $LOG_PATH$LOG_NAME
    		state='1'
    	fi
    	
    	if [ $state -eq '0' ]; then
    		echo "OK!应用服务状态检测正常"|tee -a $LOG_PATH$LOG_NAME
    	else
    		echo "ERR!应用服务状态存在异常"|tee -a $LOG_PATH$LOG_NAME
    	fi
    	
    	return $state
    }
    
    #环境检查
    environment_check()
    {
    	echo "环境检查……"|tee -a $LOG_PATH$LOG_NAME
    	state='0' #0正常	1异常
    	echo "检查redis状态……"|tee -a $LOG_PATH$LOG_NAME
    	echo "1. redis进程"|tee -a $LOG_PATH$LOG_NAME
    	if [ `ps -aux|grep redis|grep -v grep|wc -l` -ne '0' ]; then
    		echo "redis进程存在`ps -aux|grep redis|grep -v grep|wc -l`个:"|tee -a $LOG_PATH$LOG_NAME
    		echo `ps -aux|grep redis|grep -v grep`|tee -a $LOG_PATH$LOG_NAME
    	else 
    		echo "redis进程异常"|tee -a $LOG_PATH$LOG_NAME
    		state='1'
    	fi
    	echo "2. redis端口"|tee -a $LOG_PATH$LOG_NAME
    	if [ `netstat -an| grep $REDIS_PORT1|grep LISTEN|wc -l` -ne '0' ]; then
    		echo "redis端口存在`netstat -an| grep $REDIS_PORT1|grep LISTEN|wc -l`个:"|tee -a $LOG_PATH$LOG_NAME
    		echo `netstat -an| grep $REDIS_PORT1|grep LISTEN`|tee -a $LOG_PATH$LOG_NAME
    		echo "连接数:`netstat -an| grep $REDIS_PORT1|grep ESTABLISHED|wc -l`"|tee -a $LOG_PATH$LOG_NAME
    	else 
    		echo "redis端口异常"|tee -a $LOG_PATH$LOG_NAME
    		state='1'
    	fi
    	echo "3. redis测试"|tee -a $LOG_PATH$LOG_NAME
    	
    	if [ `/servers/redis-2.8.19/src/redis-cli  -p $REDIS_PORT1 -a $REDIS_PASSWD "ping"` = "PONG" ]; then
    		echo "redis测试正常"|tee -a $LOG_PATH$LOG_NAME
    	else
    		echo "redis测试异常"|tee -a $LOG_PATH$LOG_NAME
    		state='1'
    	fi
     
    	echo "java版本:"|tee -a $LOG_PATH$LOG_NAME
    	echo "`java -version`"|tee -a $LOG_PATH$LOG_NAME
    	echo "tomcat版本:"|tee -a $LOG_PATH$LOG_NAME
    	echo "`$TOMCAT_HOME/bin/version.sh`" | grep -v "Using"|tee -a $LOG_PATH$LOG_NAME
     
    	if [ $state -eq '0' ]; then
    		echo "OK!环境检查正常"|tee -a $LOG_PATH$LOG_NAME
    	else
    		echo "ERR!环境检查存在异常"|tee -a $LOG_PATH$LOG_NAME
    	fi
    	
    	return $state
    }
    
    #健康检查
    health_check()
    {
    	echo "健康检查……"|tee -a $LOG_PATH$LOG_NAME
    	environment_check
    	a=$?
    	app_check
    	b=$?
    	if [ $a -eq "0" -a $b -eq "0" ]; then
    		echo "OK!健康检查正常"|tee -a $LOG_PATH$LOG_NAME
    		return '0'
    	fi
    	echo "ERR!健康检查存在异常"|tee -a $LOG_PATH$LOG_NAME
    	return '1'
    }
    
    #启动服务
    app_start()
    {
    	echo "启动服务……"|tee -a $LOG_PATH$LOG_NAME
    	echo "检查当前登录用户……"|tee -a $LOG_PATH$LOG_NAME
    	if [ `whoami` != "app" ]; then
    		echo "ERR!当前登录用户为'`whoami`',请使用应用管理员用户登录!"|tee -a $LOG_PATH$LOG_NAME
    		return
    	fi
    	echo "当前用户:`whoami`"|tee -a $LOG_PATH$LOG_NAME
            if [ `ps -aux|grep tomcat|grep -v grep|wc -l` -ne '0' ]; then
                    echo "ERR!tomcat进程已存在,未启动服务!"|tee -a $LOG_PATH$LOG_NAME
    	return
            fi 
    	environment_check
    	a=$?
    	if [ $a -eq '1' ]; then
    		echo "ERR!环境检查不通过,未启动服务!"|tee -a $LOG_PATH$LOG_NAME
    		return
    	fi
    	echo "打开tomcat……"|tee -a $LOG_PATH$LOG_NAME
    	echo `$TOMCAT_HOME/bin/startup.sh`|tee -a $LOG_PATH$LOG_NAME
    	echo "OK!服务已启动"|tee -a $LOG_PATH$LOG_NAME
    	echo "tomcat 启动完成,是否打开日志(y/n)?"
    	read key
    	if [ $key = "y" -o $key = "Y" ]; then
    		echo "打开日志……"|tee -a $LOG_PATH$LOG_NAME
    		tail -f $TOMCAT_HOME/logs/catalina.out
    	fi
    
    }
    
    #停止服务
    app_stop()
    {
    	echo "停止服务……"
    	if [ `ps -aux|grep tomcat|grep -v grep|wc -l` -eq '0' ]; then
    		echo "tomcat进程不存在,无需停止!"|tee -a $LOG_PATH$LOG_NAME
    		return
    	fi
    #	echo `$TOMCAT_HOME/bin/shutdown.sh`|tee -a $LOG_PATH$LOG_NAME
    #	sleep 8
    #	if [ `ps -aux|grep tomcat|grep -v grep|wc -l` -eq '0' ]; then
    #		echo "成功停止服务!"|tee -a $LOG_PATH$LOG_NAME
    #		return
    #	fi
    	for pid in `ps -aux|grep tomcat|grep -v grep|awk '{print $2}'`;do
    		kill -9 $pid
    		echo "已杀死进程$pid"|tee -a $LOG_PATH$LOG_NAME
    	done
    	if [ `ps -aux|grep tomcat|grep -v grep|wc -l` -eq '0' ]; then
    		echo "OK!成功停止服务!"|tee -a $LOG_PATH$LOG_NAME
    		return
    	fi
    	echo "ERR!停止服务异常,需手动停止!"|tee -a $LOG_PATH$LOG_NAME
    }
    
    #主菜单
    menu()  
    {       
    	while [ "1" ]
    	do      
    		clear
    		echo "功能菜单:"
    		echo "1. 健康检查"
    		echo "2. 应用服务状态检测"
    		echo "3. 环境检测"
    		echo "4. 启动服务"
    		echo "5. 停止服务"
    		echo "0. 退出"
    		echo "select==>>"
    		tput cup 7 10
    		tput cnorm 
    		read choice
    		case $choice in
    		0) clear
    			break;;
    		1) echo "开始健康检查(y/n)?"
    			read key
    			if [ $key = "y" -o $key = "Y" ]; then
    				echo "+++++++++++++++++++++++++++健康检查-time:`date '+%Y-%m-%d %H:%M:%S'`+++++++++++++++++++++++++++"|tee -a $LOG_PATH$LOG_NAME
    				health_check
    			fi
    			echo "按任意键继续……"
    			read key
    			continue;;
    		2) echo "开始应用服务状态检测(y/n)?"
    			read key
    			if [ $key = "y" -o $key = "Y" ]; then
    				echo "+++++++++++++++++++++++应用服务状态检测-time:`date '+%Y-%m-%d %H:%M:%S'`+++++++++++++++++++++++"|tee -a $LOG_PATH$LOG_NAME
    				app_check
    			fi
    			echo "按任意键继续……"
    			read key
    			continue;;
    		3) echo "开始环境检测(y/n)?"
    			read key
    			if [ $key = "y" -o $key = "Y" ]; then
    				echo "+++++++++++++++++++++++++++环境检测-time:`date '+%Y-%m-%d %H:%M:%S'`+++++++++++++++++++++++++++"|tee -a $LOG_PATH$LOG_NAME
    				environment_check
    			fi
    			echo "按任意键继续……"
    			read key
    			continue;;
    		4) echo "启动服务(y/n)?"
    			read key
    			if [ $key = "y" -o $key = "Y" ]; then
    				echo "+++++++++++++++++++++++++++启动服务-time:`date '+%Y-%m-%d %H:%M:%S'`+++++++++++++++++++++++++++"|tee -a $LOG_PATH$LOG_NAME
    				app_start
    			fi
    			echo "按任意键继续……"
    			read key
    			continue;;
    		5) echo "停止服务(y/n)?"
    			read key
    			if [ $key = "y" -o $key = "Y" ]; then
    				echo "+++++++++++++++++++++++++++停止服务-time:`date '+%Y-%m-%d %H:%M:%S'`+++++++++++++++++++++++++++"|tee -a $LOG_PATH$LOG_NAME
    				app_stop
    			fi
    			echo "按任意键继续……"
    			read key
    			continue;;
    		esac    
    	done    
    }       
    
    while getopts 'dm' opt;
    do
    	case $opt in
    		d ) 
    			echo "日常周期巡检------>>>>-time:`date '+%Y-%m-%d %H:%M:%S'`"|tee -a $LOG_PATH$LOG_NAME
    			health_check >/app/tmp/daily.log
    			k=$?
    			if [ $k -eq '1' -a ! -f $FLAG ]; then
    				touch $FLAG
    				echo -e '\n\n\n' >> /app/tmp/daily.log
    				ps -aux|awk '{if($3>20) print $1,$2,$3,$4,$9,$10,$11}' >> /app/tmp/daily.log
    				echo -e '\n\n\n' >> /app/tmp/daily.log
    				tail -n 20 $TOMCAT_HOME/logs/catalina.out >> /app/tmp/daily.log
    				for mail in `cat $MAILS` ;do
    					mail -s "DS服务器周期巡检报告 `date '+%Y-%m-%d %H:%M:%S'`" "$mail" < /app/tmp/daily.log
    					echo "已发送通知邮件:$mail"|tee -a $LOG_PATH$LOG_NAME
    				done
    				app_start
    			fi
    			echo "OK!日常周期巡检完成------>>>>-time:`date '+%Y-%m-%d %H:%M:%S'`"|tee -a $LOG_PATH$LOG_NAME;;
    		m )
    			menu;;
    		* )
    			echo "Usage:`basename $0` [options] filename";;
    	esac
    done
    
    
     说明:该脚本是我为一个java程序写的综合脚本。包含应用的启停,配置crontab定时任务后可实现进程、端口等的巡检,并实现邮件告警并生成巡检日志。

    该脚本涉及options的实现(类似:ps -ef中的-ef)、记录日志、发送邮件、端口和进程的检测,可作为参考。

  4. 应用部署脚本
    #!/bin/sh
    
    
    cd /app
    echo "删除原代码..."
    rm -rf ds_website
    echo "当前工作目录:`pwd`"
    rm -rf Video-website
    echo "开始clone项目..."
    git clone https://gitee.com/dushen666/Video-website.git
    echo "拷贝覆盖项目..."
    cp -Rf Video-website ds_website
    cd ds_website
    echo "当前工作目录:`pwd`"
    echo "修改配置文件..."
    sed -i 's/^ALLOWED_HOSTS.*/ALLOWED_HOSTS = ["111.89.11.51","www.dushen6.cn",]/g' ./ds_website/settings.py
    sed -i 's/^DEBUG.*/DEBUG = False/g' ./ds_website/settings.py
    sed -i 's/^configfile.*/configfile = "\/app\/ds_website\/conf\/global.conf"/g' ./ds_website/settings.py
    
    sed -i 's/configfile = .*/configfile = "\/app\/ds_website\/conf\/global.conf"/' ./website1/cloudfiles/downloader.py
    
    sed -i 's/^PASSWORD.*/PASSWORD:pawd123--/g' ./conf/global.conf
    sed -i 's/^NAME.*/NAME:movie-website1/g' ./conf/global.conf
    
    echo "修改表结构..."
    python manage.py migrate   # 创建表结构
    
    sh /app/sbin/website1.sh restart
    
    echo '应用部署完毕!'
     说明:该脚本与(2)类似,同样是使用git clone克隆脚本到本地,使用sed修改配置文件,之后使用了Django自带的manage.py修改了表结构,最后调用了(1)脚本重启应用。