首页 > 代码库 > shell进阶教程

shell进阶教程

背景:就自己常用的shell脚本写作风格,总结了一些知识点。也是作为交接工作的一部分文档。部分内容单独写

 

#!/bin/sh
# shell脚本进阶教程
# 1.常用知识点:变量设置/日期设置/格式化输出/定义函数/函数传参/脚步传参/变量的嵌套和迭代
# 2.常用环境:/数据库监控/本地日志监控/批量处理/定期获取表数据/备份
# 3.常用循环:for/while
# 4.常用命令:sed/cut/awk/
# 5.crontab 计划任务

# 第一部分:常用知识点
# 1.【变量设置及变量替换】
# 1.1 全局变量和局部变量-在脚本中的位置来区分
# 	全局变量: 脚本中直接定义 
# 	局部变量: 在循环语句中/自定义函数中
# 从位置顺序上来说:
# 全局变量:一般在脚本的开头位置,先设置好;或者是在函数定义/循环语句之前先定义。
# 局部变量:用于局部的调用,比如循环语句中/自定义函数里;循环或函数之外调用是不生效的
# 1.2 恒定变量和动态变量-变量的值是否变化 
# 格式:
# 	恒定变量:eg: xxx=‘‘ 或者 xxx=" " 或者 或者 xxx=120 (数字或者端口的定义不用引号) 或者  xxx=" ‘‘ "   错误格式:相同引号不能套用xxx="""" 
#	动态变量:eg: xxx=`` 反引号:键盘上数字1 左边的键  
#		动态变量,两种常用设置: 
#		xxx=`shell命令` :直接把shell命令的结果赋值给xxx (xxx=`cat /data/a.txt|grep ‘3306‘` 将含有3306的行直接赋值给xxx变量)
#		xxx=`$cmd`		: cmd="hello world" (将cmd变量的内容赋值给xxx,cmd可以是恒定变量也可以是动态变量) 
#	单引号和双引号和多个引号的区别:
#		单引号:是完全引用:举两个例子:
#			1. \n :回车的意思 \t:是tab空格的意思 定义:str1=‘hello world!\nhello world!‘ 输出结果: hello world!\nhello world! 原样输出
#			2.定义SQL语句:sql1=‘select * from db1.tb1 where name="joho" and time>"$start_time"‘  由于是原样输出 start_time变量不会成功替换
#		双引号:输出字面值 但是特殊字符除外:比如转义字符:\t \r \n 变量替换:$ 
#			1.定义:str2="hello world!\nhello world!"  输出两行
#			2.sql2="select * from db1.tb1 where name=‘joho‘ and time>‘$start_time‘" 正确! 时间必须引号引起来。
#		多个引号(单引号或双引号):使用环境:定义的变量里面有:单引号,双引号,变量替换以及反引号 
#		我使用过的唯一的例子:批量建表时:
# seg01:批量打印建表语句00~99,不是很常用
for i in seq `0 99`
do
    num=`printf "%02d" $i`	#格式化0~9 变成:00~09 (两位数字)
	sql="""create table `BLDB`.`log_message_‘‘‘$num‘‘‘`(	
		`id` int,
		`name` varchar(10),
		primary key(id)
	)engine=innodb;
	"""
	# 之所以引用3个双引号是因为 :SQLyog工具导出的语句的 库名,表名,字段名 都用反引号引着。
	# 3个引号里面的变量替换,必须用3个单引号引,才能生效。
	# 手动把反引号去掉 可以不用3个双引号的。
done
# 变量例子:
user=‘admin‘
paswd=‘pssword‘
host=‘172.2.1.1‘
port=3306
dir1=‘/data/scripts‘
file1="${dir1}/log.txt" 	# 常用的变量替换
DateMark=`date ‘+%Y-%m-%d %H:%M:%S‘` # 动态变量设置当前的时间变量:2017-08-06 22:09:00 

# 2.【日期/时间设置】
# 带日期的表名/表中时间字段/脚本中的时间标志
# 时间日期的分类:当前时间/n分钟之前/n小时之前/n分钟之内/
# 时间的格式化:%Y:年	%m:月	%d:天	%H:小时		%M:分钟		%S:秒
# 2.1 当前时间:
NowTime=`date ‘+%Y-%m-%d %H:%M:%S‘`		#2017-08-06 22:09:00
NowTime=`date ‘+%Y%m%d %H%M%S‘`		#20170806 220900

# 2.2 n分钟之前: n minute ago /n day ago 
BeforeTime=`date -d‘2 minute ago‘ ‘+%Y-%m-%d %H:%M:%S‘`

# 2.3 时间范围:最近5分钟
# 无论什么环境,当前时间不计算,因为当前一分钟是不完整的
# 用于sql查询条件中时间字段的限制:格式的样式要与表中格式一致
StartTime=`date -d‘1 minute ago‘ ‘+%Y-%m-%d %H:%M:%S‘`
EndTime=`date -d‘5 minute ago‘ ‘+%Y-%m-%d %H:%M:%S‘`

# 2.4 表名:BLDB.LOG_MESSAGE_20170806
#
TB_MARK=`date ‘+%Y%m%d‘` # 20170806
TB_NAME="BLDB.LOG_MESSAGE_`date ‘+%Y%m%d‘`"	# 1.直接写入 : 不建议使用
TB_NAME="BLDB.LOG_MESSAGE_${TB_MARK}"		# 2.变量替换 :最常用:易于随时修改

# 2.5 脚步中的时间标志
#
echo "[${NowTime}] : Succeeded!" 	#执行命令后 打印输出结果

# 3.【格式化输出】:
# 好处:
#	1.输出的规范要求,比如:00~09 而不是0~9;浮点型位数要求:一般用于建100张表
#	2.左右对齐:输出美观要求,行列对齐 便于查看结果
# 常用的3中:数值/字符/浮点数
# 数值
#seg02:
for i in `seq 0 9`
do
    printf "%02d" "$i"	# 输出2位,不够用0补齐,格式化后直接打印输出 
done 
# 字符串
printf "%10s	%s" "hello!" "it‘s a dog!"	# 输出两个字段,第一个字段占10个字符,默认右对齐,不够左侧空格补齐
printf "%-10s	%s" "hello!" "it‘s a dog!"	# -:左对齐,右侧空格

# 浮点数
num=98.2245
printf "%2.2f" "$num"	# 格式化:小数点左侧2位,右侧保留2位。 用于成功率/百分比
 
# 4. 【定义函数】
# 4.1 常规函数(无传参)

#定义格式:
GET_INFO()
{
    NAME=‘Jhon‘
	echo "My name is ${NAME}"
}
# 函数调用方式:直接调用函数名
# 直接脚本中写函数名
GET_INFO

#seg03 :完整脚本
vim getinfo.sh
#-------------------------------(虚线是无用的)
#!/bin/sh	#shell脚本的开头设置
# def:function 
GET_INFO()
{
    NAME=‘Jhon‘
	echo "My name is ${NAME}"
}

# excute: function
GET_INFO
#-------------------------------

# 执行脚本及输出内容
./getinfo.sh 
My name is jhon

# 4.2 定义传参函数:
# 定义格式:带有2个参数的函数
# FUNCTION: 
GET_INFO()
{
    PORT=$1
	IP=$2
	echo "MYSQL PORT IS : ${PORT} AND MySQL HOST IS ${IP}"
	result=`mysql -uadmin -p‘password‘ -h${IP} -P${PORT} -NBe"SELECT * FROM URCS_BLDB.LOG_MESSAGE_01;"` # 读取表数据,将输出结果赋值给result变量
	echo "$result" >> /data/log.txt #将结果写入指定文件
}

# 调用方式:函数名 参数1 参数2
GET_INFO 3306 ‘172.21.1.1‘ 
GET_INFO 3307 ‘172.21.1.2‘
# 以上是两次调用函数。适用于相似的结果输出,不同的传入变量
# 通常函数传参 和脚本传参 相结合 做监控项时 非常给力。现网监控就是如此做的

# 5. 【脚本传参】
# 环境:使用一个脚本,处理相似的操作类。比如:不同MySQL用端口来区分,每个MySQL监控项是一样的。唯一不同的是端口,因此端口就可以作为变量,作为脚本的参数 

# 脚本调用方式:1个脚本参数
./getinfo.sh 3306	# 脚本中的所有输出内容都是3306数据库相关的
./getinfo.sh 3307	# 脚本中的所有输出内容都是3307数据库相关的

# 脚本参数在脚本中的体现
# 脚本开头位置,设置脚本参数变量
db_port=$1	# $1 代表脚本后面的第一个参数;$2 代表脚本后面跟的第二个参数;$0 代表脚本本身
# 这种变量赋值,是将 脚本参数原样的赋值给:db_port。脚本中就可以直接引用db_port 这个变量来代表端口。
# 如果是字符串作为脚本参数,字符串里有特殊符号或者空格,需要用单引号 引起来。

#seg04: 脚本通过传入端口,来监控该数据库实例的 某个监控项

#!/bin/sh
db_port=$1
db_item=$2	# 代表监控项:比如 连接数,主从状态。。。。;可以看做是一个触发动作,后面会讲,总之这个是变化的量

echo "$db_port : $db_item"

# 执行方式:./getinfo.sh 3306 conn
# 输出结果:3306 :conn


# 6. 【脚本变量的嵌套】

# 6.1 常规的 嵌套

# seg05:
#!/bin/sh
# SQL查询的where条件
S1=‘timeout‘
S2=‘error code‘
......
# 定义函数:根据传入的条件生成SQL语句;将语句赋值给一个变量,并将变量传入另一个SQL查询函数中,将查询结果返回或者写入文件
CREATE_SQL()
{
    S=$1
	SQL=‘‘‘SELECT * FROM URCS_BLDB.LOG_MESSAGE_01 WHERE message like "%‘‘‘$S‘‘‘%" ;‘‘‘	# 关于引号的使用,目的是生成的SQL是正常就行。
	echo $SQL 
}

# 定义SQL查询函数
GET_INFO()
{
    sql=$1
	mysql -uadmin -p‘pasd‘ -h172.21.1.1 -P3306 -NBe"$sql";
}

SQL1=`CREATE_SQL "$S1"`	# 通过函数生成SQL语句,赋值给变量SQL1
SQL1_DATA=http://www.mamicode.com/`GET_INFO"$SQL1"`	#传入SQL1,函数产生查询结果,并赋值给SQL1_DATA变量。
echo $SQL1_DATA >SQL1.txt 	# 将变量内容写入文件中


# 第二部分
# 常用环境 
# 1. 【数据库性能参数监控】
# 监控脚本思路:
# (1).监控项很多,避免每次都访问数据库,因此,通过:show global status\G 将结果写入一个文件
# (2).每个监控项,定义一个函数,从文件读取信息,经过处理返回
# (3).通过脚本传参的方式 触发函数并返回值 ./xxx.sh  action
# (4).多实例情况,通过端口来区分,复用脚本。通过脚本传参来实现:./xxx.sh port action 
# (5).有n个监控项,一分钟会触发脚本n次。 只需在第一次触发时,就生成文件。因此需要定义函数,将status信息写入文件,并检查,文件是否存在,存在跳过,不存在,读取信息并写入。
# (6).生成文件以时间标志,因此需要清理机制。定义一个清理函数作为监控项,在zabbix中每隔30分钟调用一次。

# 简写脚本:主从状态的监控
# seg06:
#!/bin/sh
#. /etc/profile
#. /etc/bashrc
# name : slave.sh 
#############
# 定义MySQL命令 变量:
MYSQL_BIN="/usr/local/mysql5715/bin/mysql -umonitor -pmonitor"	
# 定义生成文件的时间标志
INFO_TIME=`date +%m%d%H%M`
# 定义输出文件目录
INFO_DIR="/usr/local/zabbix/scripts/tmp/"
# 定义MySQL的端口
MYSQL_PORT="$1"
# 定义生成的文件名
INFO_FILE="/usr/local/zabbix/scripts/tmp/SLAVE_INFO_${MYSQL_PORT}_${INFO_TIME}"
#############
# 定义函数,判断文件是否存在,不存在,获取slave信息,并写入文件;存在就跳过
GET_INFO(){
if [[ -f ${INFO_FILE} ]] ; then
    break
else
    ${MYSQL_BIN} -S /data/socket/mysql${MYSQL_PORT}.sock -e ‘SHOW SLAVE STATUS \G‘ >${INFO_FILE}
fi
}

# 定义从库延时函数
Seconds_Behind_Master(){
    SBM=`/bin/egrep -w ‘Seconds_Behind_Master‘ ${INFO_FILE} | awk ‘{print $2}‘ `    
    echo ${SBM}
}
# 定义SQL线程状态
Slave_SQL_Running(){
    THD_SQL=`/bin/egrep -w ‘Slave_SQL_Running‘ ${INFO_FILE} | awk ‘{print $2}‘ `    
    if [[ ${THD_SQL} = ‘No‘ ]] ; then 
        echo 0
    else
        echo 1
    fi
}
# 定义IO线程状态
Slave_IO_Running(){
    THD_IO=`/bin/egrep -w ‘Slave_IO_Running‘ ${INFO_FILE} | awk ‘{print $2}‘ `
    if [[ ${THD_IO} = ‘No‘ ]] ; then 
        echo 0
    else
        echo 1
    fi
}


# 定义函数 判断是主还是从
R_EMLP(){
    RMLP=`/bin/egrep -w ‘Read_Master_Log_Pos‘ ${INFO_FILE} | awk ‘{print $2}‘ `    
    EMLP=`/bin/egrep -w ‘Exec_Master_Log_Pos‘ ${INFO_FILE} | awk ‘{print $2}‘ `    
    R_EMLP=$[${RMLP}-${EMLP}]
    echo ${R_EMLP}
}

#######################
GET_INFO	# 每次执行脚本都触发:获取信息的函数
# 下面两个变量 如果=4,说明是主库,返回值:3,zabbix判断认为是主,不会触发报警
RMLP=`/bin/egrep -w ‘Read_Master_Log_Pos‘ ${INFO_FILE} | awk ‘{print $2}‘ `
RELP=`/bin/egrep -w ‘Relay_Log_Pos‘ ${INFO_FILE} | awk ‘{print $2}‘ `
# 下面的:case $2 in ,通过判断脚本传的第二个参数,是哪个,就触发对应的函数,并返回对应的值
if [[ ${RMLP} == 4 ]] && [[ ${RELP} == 4 ]] ; then
        case $2 in
        sla_sbm)
        echo 3
        ;;
        thd_sql)
        echo 3
        ;;
        thd_io)
        echo 3
        ;;
        sla_rem)
        echo 3
        ;;
        *)
        esac
    else
        case $2 in	
        sla_sbm)
        Seconds_Behind_Master
        ;;
        thd_sql)
        Slave_SQL_Running
        ;;
        thd_io)
        Slave_IO_Running
        ;;
        sla_rem)
        R_EMLP
        ;;
        *)
        esac
fi

# 定义清理函数
# 定义清理函数:每隔30分钟清理一次,每次把前一分钟之前的都清理掉
GLO_CLEAR(){
/bin/find ${INFO_DIR}/*_INFO_* -ctime -1 | xargs rm -f
FILE_NUM=`/bin/find ${INFO_DIR} -name "*_INFO_*" -ctime -1 | wc -l`
if [[ ${FILE_NUM} == 0 ]] ; then
    echo 1
else
    echo 0 
fi
}
#测试:./slave.sh 3306 thd_sql 会得到一个返回值

# 2.【本地log日志,关键字查询】
# SQL查询本地数据库表,含有错误关键字的行总数,
# 脚本思路
# (1).错误关键字 查询多,以标准格式,放到单独的文件里。脚本通过循环读取该文件,进行SQL生成
# (2).SQL 查询的结果,存放到对应的文件
# (3).输出信息中字段条件多,所以通过其它脚本处理该信息
# (4).
# 简写脚本
#!/bin/sh
# 定义变量
HOST="localhost"
PORT=9306
USER="root"
PSWD="root"
DB_NAME="LogDB"
TB_NAME="LOG_"`date +‘%Y%m%d‘`
MYSQL_CMD="/home/mysql/Percona-Server/bin/mysql -u${USER} -p${PSWD} -h${HOST} -P${PORT} -S /tmp/mysql.sock"
# 时间变量设置,取值范围前一分钟的数据
TIME0=`date +‘%Y-%m-%d %H:%M‘` 	# 当前时间 
TIME1=`date -d "1 minutes ago" +‘%Y-%m-%d %H:%M‘` 	# 前一分钟:2017-08-05 10:34 秒钟在SQL定义时添加
# 定义结果的输出文件的目录:
# tmp_dir
FILE_DIR="/data/scripts/shelldir"

# 定义错误关键字的格式 文件
KeyFile=‘/data/scripts/KeyFile.txt‘
内容:
####too many connections####connect.txt####
####timeout####timeout.txt####

#定义SQL生成函数:参数:关键字
GET_SQL()
{
    Key=$1
    SQL=‘‘‘SELECT A.ServiceName,IFNULL(B.count_error,0) AS count_total FROM (select distinct(ServiceName) from ‘‘‘${DB_NAME}.${TB_NAME}‘‘‘ where (Time >="‘‘‘${TIME1}‘:00‘‘‘‘" AND Time<="‘‘‘${TIME0}‘:59‘‘‘‘")) AS A left join (select ServiceName,count(*) AS count_error from ‘‘‘${DB_NAME}.${TB_MARK}‘‘‘ WHERE (Time >="‘‘‘${TIME1}‘:00‘‘‘‘" AND Time <="‘‘‘${TIME1}‘:59‘‘‘‘") AND (Message LIKE "%‘‘‘${Key}‘‘‘%" OR Error LIKE "%‘‘‘${Key}‘‘‘%") group by ServiceName) AS B ON A.ServiceName=B.ServiceName group by A.ServiceName;‘‘‘
    echo $SQL
}

# 定义SQL查询函数:参数:SQL语句,文件名
GET_INFO()
{
	SQL=$1
	FILE=$2
    ${MYSQL_CMD} -NBe "${SQL}" >>${FILE_DIR}/${FILE}
}

# 循环读取关键字文件,一次进行SQL生成,SQL查询
# 主程序
cat ${KeyFile}| while read line
do
	# 截取关键字和输出文件名字	
    Key=`echo ${line}|awk -F‘####‘ ‘{print $1}‘`
	Outfile=`echo ${line}|awk -F‘####‘ ‘{print $2}‘`
	# 生成SQL语句
    SQL=`GET_SQL "$Key"`
	# 查询结果写入文件
	GET_INFO "$SQL" "$Outfile" 	
done

# 3.【批量处理】
# 常用 
for i in `seq 0 99`
do
    echo "truncate table xxxx_$i"
done

#命令行:for i in `seq 1 100`;do  echo "truncate table xxxx_$i;";done 

# 4种批量建表程序

##create 100 tables
#!/bin/sh
#
db_name=‘USE RenmaiInfluenceDB‘

for i in {0 99};
do 
    len=`expr length $i`
    if [ $len -eq 2 ];then
	    num=$i
	else
	    num="0${i}"
	fi
    echo ‘‘‘
CREATE TABLE `logRegister_‘‘‘$num‘‘‘` (
  `Id` int(10) NOT NULL AUTO_INCREMENT COMMENT ‘自增id‘,
  `AwardId` int(10) NOT NULL COMMENT ‘奖品Id‘,
  `UserId` int(10) NOT NULL COMMENT ‘中奖用户‘,
  `CourierName` varchar(20) DEFAULT NULL COMMENT ‘快递名‘,
  `CourierNumber` varchar(40) DEFAULT NULL COMMENT ‘运单号‘,
  `CreateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘中奖时间‘,
  PRIMARY KEY (`Id`),
  KEY `IX_SignLotteryWinner_UserId_CreateDate` (`UserId`,`CreateDate`)
) ENGINE=InnoDB AUTO_INCREMENT=1602 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT=‘打卡中奖表‘;

    ‘‘‘
	echo 
done	

========================================================================================================
#!/bin/sh
#creat tables

for i in `seq 1 15`
do
    var=`printf "%02d\n" $i`
   
    echo ‘‘‘
CREATE TABLE `logRegister_‘‘‘$var‘‘‘` (
  `Id` int(10) NOT NULL AUTO_INCREMENT COMMENT ‘自增id‘,
  `AwardId` int(10) NOT NULL COMMENT ‘奖品Id‘,
  `UserId` int(10) NOT NULL COMMENT ‘中奖用户‘,
  `CourierName` varchar(20) DEFAULT NULL COMMENT ‘快递名‘,
  `CourierNumber` varchar(40) DEFAULT NULL COMMENT ‘运单号‘,
  `CreateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘中奖时间‘,
  PRIMARY KEY (`Id`),
  KEY `IX_SignLotteryWinner_UserId_CreateDate` (`UserId`,`CreateDate`)
) ENGINE=InnoDB AUTO_INCREMENT=1602 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT=‘打卡中奖表‘;
‘‘‘ 
  
    echo

done

============================================================================
#!/bin/sh 

# 需要将 SQL 中的" ` " 符号删除

for i in `seq 0 1 99`
do
    STEP=`printf %02d ${i}`
    create_tab_sql="
    CREATE TABLE logRegister_${STEP} (
    Id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘表id‘,
    UserId int(11) NOT NULL COMMENT ‘用户id‘,
    UserName varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT ‘用户名‘,
    LotteryNumber tinyint(1) NOT NULL COMMENT ‘今日剩余可抽奖次数‘,
    StateTime date NOT NULL COMMENT ‘当前日期‘,
    lottery tinyint(1) NOT NULL DEFAULT ‘0‘,
    award_times tinyint(1) NOT NULL DEFAULT ‘0‘,
    FeitionID int(11) DEFAULT NULL COMMENT ‘用户飞信号码‘,
    PRIMARY KEY (Id),
    KEY idx (UserId) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT=‘每日用户可游戏次数纪录表‘;"

    echo -e ${create_tab_sql}
    
    echo
done
===================================================================================
#!/usr/bin/python

import string

for i in range(7,13):
    sql = "CREATE TABLE `UgcFeedContent_2015%02d` LIKE `UgcFeedContent_201503`;" % i
    print(sql)
for i in range(1,13):
    sql = "CREATE TABLE `UgcFeedContent_2016%02d` LIKE `UgcFeedContent_201503`;" % i
    print(sql)

========================================

其余几部分 单独写

  

 

shell进阶教程