Xtrabackup定时备份mysql数据库脚本

定时备份MySQL数据库

脚本内容:

#!/bin/bash

## 备份计划任务
## 
## 每天凌晨1:30一次全量备份
## 每天间隔1小时一次增量备份
## 30 1 * * * backup.sh full
## 00 * * * * backup.sh inc
##
##  恢复数据步骤:
##  (1)、查看备份日志,找到全量备份和增量备份的关系(注意增量备份的顺序)
##
##  cat ${BACKUP_BASE_DIR}/${INC_BASE_LIST}
##  (2)、全量备份
##  innobackupex --defaults-file=/etc/my.cnf --apply-log ${BACKUP_BASE_DIR}/full_dir
##
##  (3)、第一个增量
##  innobackupex --defaults-file=/etc/my.cnf --apply-log ${BACKUP_BASE_DIR}/full_dir 
##  --incremental-dir=${BACKUP_BASE_DIR}/one_inc_dir
##
##  (4)、第二个增量
##  innobackupex --defaults-file=/etc/my.cnf --apply-log ${BACKUP_BASE_DIR}/full_dir 
##  --incremental-dir=${BACKUP_BASE_DIR}/two_inc_dir
##
##  (5)、恢复数据
##  innobackupex --defaults-file=/etc/my.cnf --copy-back ${BACKUP_BASE_DIR}/full_dir

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

BACKUP_BASE_DIR="/backup/xtrabackup"
INC_BASE_LIST="${BACKUP_BASE_DIR}/inc_list.txt"
XTRABACKUP_PATH="/usr/local/xtrabackup/bin/innobackupex"

MYSQL_CNF="/etc/my.cnf"
MYSQL_HOSTNAME=127.0.0.1
MYSQL_USERNAME=root
MYSQL_PASSWORD=w7tQ5NNWWRk

LOCK_FILE=/tmp/innobackupex.lock
THREAD=3

mkdir -p ${BACKUP_BASE_DIR}
CURRENT_BACKUP_PATH="${BACKUP_BASE_DIR}/$(date +%F_%H-%M)"
[[ -d ${CURRENT_BACKUP_PATH} ]] && CURRENT_BACKUP_PATH="${BACKUP_BASE_DIR}/$(date +%F_%H-%M-%S)"

print_help(){
    echo "--------------------------------------------------------------"
    echo "Usage: $0 full | inc | help               "
    echo "--------------------------------------------------------------"
    exit 1
}

[[ $# -lt 1 || "$1" == "help" ]] && print_help

[[ -f "$LOCK_FILE" ]] && echo -e "Usage: rm -f $LOCK_FILEnUsage: chattr -i $LOCK_FILE && rm -f $LOCK_FILE" && exit 1

FullBackup(){
    touch $LOCK_FILE
    chattr +i $LOCK_FILE
    local rc=0
    ${XTRABACKUP_PATH} 
    --defaults-file=${MYSQL_CNF} 
    --user=${MYSQL_USERNAME} 
    --password=${MYSQL_PASSWORD} 
    --host=${MYSQL_HOSTNAME} 
    --parallel=${THREAD} 
    --no-timestamp ${CURRENT_BACKUP_PATH} > ${CURRENT_BACKUP_PATH}_full.log 2>&1
    grep ".* completed OK!" ${CURRENT_BACKUP_PATH}_full.log > /dev/null 2>&1
    if [ $? -ne 0 ];then
        rc=1
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && rm -rf ${CURRENT_BACKUP_PATH}
    else
        echo "NULL|${CURRENT_BACKUP_PATH}|full" >> ${INC_BASE_LIST}
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && chattr +i ${CURRENT_BACKUP_PATH} || rc=1
    fi
    chattr -i ${LOCK_FILE}
    rm -f $LOCK_FILE
    chattr +a ${INC_BASE_LIST}
    return $rc
}

IncBackup(){
    touch $LOCK_FILE
    chattr +i $LOCK_FILE
    local rc=0
    PREV_BACKUP_DIR=$(sed '/^$/d' ${INC_BASE_LIST} | tail -1 | awk -F '|' '{print $2}')
    ${XTRABACKUP_PATH} 
    --defaults-file=${MYSQL_CNF} 
    --user=${MYSQL_USERNAME} 
    --password=${MYSQL_PASSWORD} 
    --host=${MYSQL_HOSTNAME} 
    --no-timestamp --incremental ${CURRENT_BACKUP_PATH} 
    --incremental-basedir=${PREV_BACKUP_DIR} > ${CURRENT_BACKUP_PATH}_inc.log 2>&1
    grep ".* completed OK!" ${CURRENT_BACKUP_PATH}_inc.log > /dev/null 2>&1
    if [ $? -ne 0 ];then
        rc=1
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && rm -rf ${CURRENT_BACKUP_PATH}
    else
        echo "${PREV_BACKUP_DIR}|${CURRENT_BACKUP_PATH}|inc" >> ${INC_BASE_LIST}
        [[ -d ${CURRENT_BACKUP_PATH} && $(pwd) != "/" ]] && chattr +i ${CURRENT_BACKUP_PATH} || rc=1
    fi
    chattr -i ${LOCK_FILE}
    rm -f $LOCK_FILE
    chattr +a ${INC_BASE_LIST}
    return $rc
}

## 全量备份
if [ "$1" == "full" ];then
    FullBackup
fi

## 增量备份
if [ "$1" == "inc" ];then
    ## 判断上一次备份是否存在,无则进行全量备份
    if [[ ! -f ${INC_BASE_LIST} || $(sed '/^$/d' ${INC_BASE_LIST} | wc -l) -eq 0 ]];then
        FullBackup
    else
        IncBackup
    fi
fi

## 删除14天前的备份
if [[ -d ${BACKUP_BASE_DIR} && $(pwd) != "/" ]];then
    find ${BACKUP_BASE_DIR} -name "$(date -d '14 days ago' +'%F')_*" | xargs chattr -i
    find ${BACKUP_BASE_DIR} -name "$(date -d '14 days ago' +'%F')_*" | xargs rm -rf
fi

加入计划任务

crontab -e
30 1 * * * /home/sh/backup.sh full
00 */6 * * * /home/sh/backup.sh inc
# mysql 

借助tcpdump统计http请求

这里所说的统计http请求,是指统计QPS(每秒请求数),统计前十条被访问最多的url。一般做这样的统计时,我们经常会使用网站访问日志来统计。当我们来到一个陌生的服务器环境,需要立即统计当前前十条被访问最多的url,来初步确定是否存在攻击行为,使用tcpdump则简单得多,因为我们不需要关心网站日志在哪,不需要考虑网站日志有没有开启之类的问题,直接用tcpdump捕捉当前的http包,再进一步过滤,就会得出我们想要的统计。此功能已集成到EZHTTP,下面是效果图:
Shell
下面介绍其统计方法。
1、捕捉10秒的数据包。

  1. tcpdump -i eth0 tcp[20:2]=0x4745 or tcp[20:2]=0x504f -w /tmp/tcp.cap -s 512 2>&1 &
  2. sleep 10
  3. kill `ps aux | grep tcpdump | grep -v grep | awk ‘{print $2}’`

此命令表示监控网卡eth0,捕捉tcp,且21-22字节字符为GE或者PO,表示匹配GET或者POST请求的数据包,并写到/tmp/tcp.cap文件。
2、这时候我们得到最新10秒的二进制数据包文件,我们下一步就是通过strings命令来找出GET/POST的url以及Host。

  1. strings /tmp/tcp.cap | grep -E "GET /|POST /|Host:" | grep –no-group-separator -B 1 "Host:" | grep –no-group-separator -A 1 -E "GET /|POST /" | awk ‘{url=$2;getline;host=$2;printf ("%sn",host""url)}’ > url.txt

此命令是本文的关键,通过strings显示二进制文件tcp.cap所有可打印字符,然后通过grep和awk过滤出http请求,并把拼接得到的url(包括域名+uri)写进一个文件url.txt。
3、这时我们拿到了近10秒钟所有的访问url,接下来的统计就容易得出,比如:
统计QPS:

  1. (( qps=$(wc -l /tmp/url.txt | cut -d’ ‘ -f 1) / 10 ))

排除静态文件统计前10访问url:

  1. grep -v -i -E ".(gif|png|jpg|jpeg|ico|js|swf|css)" /tmp/url.txt | sort | uniq -c | sort -nr | head -n 10

网络分析shell脚本(实时流量+连接统计)

介绍一个强大的分析网络的shell脚本,此脚本是从EZHTTP拆分出来的,觉得有必要单独介绍下。
脚本运行效果截图:
Shell

Shell

Shell
此脚本包含的功能有:

  • 1、实时监控任意网卡的流量
  • 2、统计10秒内平均流量
  • 3、统计每个端口在10秒内的平均流量,基于客户端和服务端端口统计。可以看出哪些端口占流量比较大,对于web服务器,一般是80端口。其它端口受到攻击时,也有可能其它端口流量比较大。所以此功能可以帮助我们端口流量是否正常。
  • 4、统计在10s内占用带宽最大的前10个ip。此项功能可以帮助我们来查出是否有恶意占用带宽的ip。
  • 5、统计连接状态。此项功能可以让我们看出哪些连接状态比较大。如果SYN-RECV状态比较多的话,有可以受到半连接攻击。如果ESTABLISED非常大,但通过日志发现没有那么多请求,或者通过tcpdump发现大量ip只建立连接不请求数据的话,可能是受到了全连接攻击,这时候如果你使用的是nginx服务器,可以在配置文件增加listen 80 deferred来防止。
  • 6、统计各端口连接状态。当可能受到攻击时,此项功能可以帮助我们发现是哪个端口受到攻击。
  • 7、统计端口为80且状态为ESTAB连接数最多的前10个IP。此项功能可以帮助我们来找出创建连接过多的Ip,进而屏蔽。
  • 8、统计端口为80且状态为SYN-RECV连接数最多的前10个IP。当受到半连接攻击时,此项功能可以帮助我们找到恶意ip。

用到的网络分析工具:

  • 1、tcpdump:此脚本用tcpdump来统计基于ip或基于端口的流量。
  • 2、ss: 此脚本用ss命令来统计连接状态,实际使用发现ss比netstat高效得多。
  • 3、/proc/net/dev,用来统计指定网卡的流量。

脚本下载地址:http://devops.webres.wang/wp-content/uploads/2014/06/network-analysis.sh
下面贴出完整的脚本:

  1. #!/bin/bash
  2.  
  3. #write by zhumaohai(admin#webres.wang)
  4. #author blog: devops.webres.wang
  5.  
  6.  
  7. #显示菜单(单选)
  8. display_menu(){
  9. local soft=$1
  10. local prompt="which ${soft} you’d select: "
  11. eval local arr=(${${soft}_arr[@]})
  12. while true
  13. do
  14.     echo -e "#################### ${soft} setting ####################nn"
  15.     for ((i=1;i<=${#arr[@]};i++ )); do echo -e "$i) ${arr[$i-1]}"; done
  16.     echo
  17.     read -p "${prompt}" $soft
  18.     eval local select=$$soft
  19.     if [ "$select" == "" ] || [ "${arr[$soft-1]}" == ""  ];then
  20.         prompt="input errors,please input a number: "
  21.     else
  22.         eval $soft=${arr[$soft-1]}
  23.         eval echo "your selection: $$soft"             
  24.         break
  25.     fi
  26. done
  27. }
  28.  
  29. #把带宽bit单位转换为人类可读单位
  30. bit_to_human_readable(){
  31.     #input bit value
  32.     local trafficValue=$1
  33.  
  34.     if [[ ${trafficValue%.*} -gt 922 ]];then
  35.         #conv to Kb
  36.         trafficValue=`awk -v value=$trafficValue ‘BEGIN{printf "%0.1f",value/1024}’`
  37.         if [[ ${trafficValue%.*} -gt 922 ]];then
  38.             #conv to Mb
  39.             trafficValue=`awk -v value=$trafficValue ‘BEGIN{printf "%0.1f",value/1024}’`
  40.             echo "${trafficValue}Mb"
  41.         else
  42.             echo "${trafficValue}Kb"
  43.         fi
  44.     else
  45.         echo "${trafficValue}b"
  46.     fi
  47. }
  48.  
  49. #判断包管理工具
  50. check_package_manager(){
  51.     local manager=$1
  52.     local systemPackage=”
  53.     if cat /etc/issue | grep -q -E -i "ubuntu|debian";then
  54.         systemPackage=’apt’
  55.     elif cat /etc/issue | grep -q -E -i "centos|red hat|redhat";then
  56.         systemPackage=’yum’
  57.     elif cat /proc/version | grep -q -E -i "ubuntu|debian";then
  58.         systemPackage=’apt’
  59.     elif cat /proc/version | grep -q -E -i "centos|red hat|redhat";then
  60.         systemPackage=’yum’
  61.     else
  62.         echo "unkonw"
  63.     fi
  64.  
  65.     if [ "$manager" == "$systemPackage" ];then
  66.         return 0
  67.     else
  68.         return 1
  69.     fi   
  70. }
  71.  
  72.  
  73. #实时流量
  74. realTimeTraffic(){
  75.     local eth=""
  76.     local nic_arr=(`ifconfig | grep -E -o "^[a-z0-9]+" | grep -v "lo" | uniq`)
  77.     local nicLen=${#nic_arr[@]}
  78.     if [[ $nicLen -eq 0 ]]; then
  79.         echo "sorry,I can not detect any network device,please report this issue to author."
  80.         exit 1
  81.     elif [[ $nicLen -eq 1 ]]; then
  82.         eth=$nic_arr
  83.     else
  84.         display_menu nic
  85.         eth=$nic
  86.     fi   
  87.  
  88.     local clear=true
  89.     local eth_in_peak=0
  90.     local eth_out_peak=0
  91.     local eth_in=0
  92.     local eth_out=0
  93.  
  94.     while true;do
  95.         #移动光标到0:0位置
  96.         printf "33[0;0H"
  97.         #清屏并打印Now Peak
  98.         [[ $clear == true ]] && printf "33[2J" && echo "$eth——–Now——–Peak———–"
  99.         traffic_be=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  100.         sleep 2
  101.         traffic_af=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  102.         #计算速率
  103.         eth_in=$(( (${traffic_af[0]}-${traffic_be[0]})*8/2 ))
  104.         eth_out=$(( (${traffic_af[1]}-${traffic_be[1]})*8/2 ))
  105.         #计算流量峰值
  106.         [[ $eth_in -gt $eth_in_peak ]] && eth_in_peak=$eth_in
  107.         [[ $eth_out -gt $eth_out_peak ]] && eth_out_peak=$eth_out
  108.         #移动光标到2:1
  109.         printf "33[2;1H"
  110.         #清除当前行
  111.         printf "33[K"   
  112.         printf "%-20s %-20sn" "Receive:  $(bit_to_human_readable $eth_in)" "$(bit_to_human_readable $eth_in_peak)"
  113.         #清除当前行
  114.         printf "33[K"
  115.         printf "%-20s %-20sn" "Transmit: $(bit_to_human_readable $eth_out)" "$(bit_to_human_readable $eth_out_peak)"
  116.         [[ $clear == true ]] && clear=false
  117.     done
  118. }
  119.  
  120. #流量和连接概览
  121. trafficAndConnectionOverview(){
  122.     if ! which tcpdump > /dev/null;then
  123.         echo "tcpdump not found,going to install it."
  124.         if check_package_manager apt;then
  125.             apt-get -y install tcpdump
  126.         elif check_package_manager yum;then
  127.             yum -y install tcpdump
  128.         fi
  129.     fi
  130.  
  131.     local reg=""
  132.     local eth=""
  133.     local nic_arr=(`ifconfig | grep -E -o "^[a-z0-9]+" | grep -v "lo" | uniq`)
  134.     local nicLen=${#nic_arr[@]}
  135.     if [[ $nicLen -eq 0 ]]; then
  136.         echo "sorry,I can not detect any network device,please report this issue to author."
  137.         exit 1
  138.     elif [[ $nicLen -eq 1 ]]; then
  139.         eth=$nic_arr
  140.     else
  141.         display_menu nic
  142.         eth=$nic
  143.     fi
  144.  
  145.     echo "please wait for 10s to generate network data…"
  146.     echo
  147.     #当前流量值
  148.     local traffic_be=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  149.     #tcpdump监听网络
  150.     tcpdump -v -i $eth -tnn > /tmp/tcpdump_temp 2>&1 &
  151.     sleep 10
  152.     clear
  153.     kill `ps aux | grep tcpdump | grep -v grep | awk ‘{print $2}’`
  154.  
  155.     #10s后流量值
  156.     local traffic_af=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  157.     #打印10s平均速率
  158.     local eth_in=$(( (${traffic_af[0]}-${traffic_be[0]})*8/10 ))
  159.     local eth_out=$(( (${traffic_af[1]}-${traffic_be[1]})*8/10 ))
  160.     echo -e "33[32mnetwork device $eth average traffic in 10s: 33[0m"
  161.     echo "$eth Receive: $(bit_to_human_readable $eth_in)/s"
  162.     echo "$eth Transmit: $(bit_to_human_readable $eth_out)/s"
  163.     echo
  164.  
  165.     local regTcpdump=$(ifconfig | grep -A 1 $eth | awk -F'[: ]+’ ‘$0~/inet addr:/{printf $4"|"}’ | sed -e ‘s/|$//’ -e ‘s/^/(/’ -e ‘s/$/)\\.[0-9]+:/’)
  166.  
  167.     #新旧版本tcpdump输出格式不一样,分别处理
  168.     if awk ‘/^IP/{print;exit}’ /tmp/tcpdump_temp | grep -q ")$";then
  169.         #处理tcpdump文件
  170.         awk ‘/^IP/{print;getline;print}’ /tmp/tcpdump_temp > /tmp/tcpdump_temp2
  171.     else
  172.         #处理tcpdump文件
  173.         awk ‘/^IP/{print}’ /tmp/tcpdump_temp > /tmp/tcpdump_temp2
  174.         sed -i -r ‘s#(.*: [0-9]+))(.*)#1n    2#’ /tmp/tcpdump_temp2
  175.     fi
  176.     
  177.     awk ‘{len=$NF;sub(/)/,"",len);getline;print $0,len}’ /tmp/tcpdump_temp2 > /tmp/tcpdump
  178.  
  179.     #统计每个端口在10s内的平均流量
  180.     echo -e "33[32maverage traffic in 10s base on server port: 33[0m"
  181.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line="clients > "$8"."$9"."$10"."$11":"$12}else{line=$2"."$3"."$4"."$5":"$6" > clients"};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  182.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  183.         echo "$a $b $c $(bit_to_human_readable $d)/s"
  184.     done
  185.     echo -ne "33[11A"
  186.     echo -ne "33[50C"
  187.     echo -e "33[32maverage traffic in 10s base on client port: 33[0m"
  188.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line=$2"."$3"."$4"."$5":"$6" > server"}else{line="server > "$8"."$9"."$10"."$11":"$12};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  189.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  190.             echo -ne "33[50C"
  191.             echo "$a $b $c $(bit_to_human_readable $d)/s"
  192.     done   
  193.         
  194.     echo
  195.  
  196.     #统计在10s内占用带宽最大的前10个ip
  197.     echo -e "33[32mtop 10 ip average traffic in 10s base on server: 33[0m"
  198.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line=$2"."$3"."$4"."$5" > "$8"."$9"."$10"."$11":"$12}else{line=$2"."$3"."$4"."$5":"$6" > "$8"."$9"."$10"."$11};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  199.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  200.         echo "$a $b $c $(bit_to_human_readable $d)/s"
  201.     done
  202.     echo -ne "33[11A"
  203.     echo -ne "33[50C"
  204.     echo -e "33[32mtop 10 ip average traffic in 10s base on client: 33[0m"
  205.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line=$2"."$3"."$4"."$5":"$6" > "$8"."$9"."$10"."$11}else{line=$2"."$3"."$4"."$5" > "$8"."$9"."$10"."$11":"$12};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  206.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  207.         echo -ne "33[50C"
  208.         echo "$a $b $c $(bit_to_human_readable $d)/s"
  209.     done
  210.  
  211.     echo
  212.     #统计连接状态
  213.     local regSS=$(ifconfig | grep -A 1 $eth | awk -F'[: ]+’ ‘$0~/inet addr:/{printf $4"|"}’ | sed -e ‘s/|$//’)
  214.     ss -an | grep -v -E "LISTEN|UNCONN" | grep -E "$regSS" > /tmp/ss
  215.     echo -e "33[32mconnection state count: 33[0m"
  216.     awk ‘NR>1{sum[$(NF-4)]+=1}END{for (state in sum){print state,sum[state]}}’ /tmp/ss | sort -k 2 -nr
  217.     echo
  218.     #统计各端口连接状态
  219.     echo -e "33[32mconnection state count by port base on server: 33[0m"
  220.     awk ‘NR>1{sum[$(NF-4),$(NF-1)]+=1}END{for (key in sum){split(key,subkey,SUBSEP);print subkey[1],subkey[2],sum[subkey[1],subkey[2]]}}’ /tmp/ss | sort -k 3 -nr | head -n 10   
  221.     echo -ne "33[11A"
  222.     echo -ne "33[50C"
  223.     echo -e "33[32mconnection state count by port base on client: 33[0m"
  224.     awk ‘NR>1{sum[$(NF-4),$(NF)]+=1}END{for (key in sum){split(key,subkey,SUBSEP);print subkey[1],subkey[2],sum[subkey[1],subkey[2]]}}’ /tmp/ss | sort -k 3 -nr | head -n 10 | awk ‘{print "33[50C"$0}’   
  225.     echo   
  226.     #统计端口为80且状态为ESTAB连接数最多的前10个IP
  227.     echo -e "33[32mtop 10 ip ESTAB state count at port 80: 33[0m"
  228.     cat /tmp/ss | grep ESTAB | awk -F'[: ]+’ ‘{sum[$(NF-2)]+=1}END{for (ip in sum){print ip,sum[ip]}}’ | sort -k 2 -nr | head -n 10
  229.     echo
  230.     #统计端口为80且状态为SYN-RECV连接数最多的前10个IP
  231.     echo -e "33[32mtop 10 ip SYN-RECV state count at port 80: 33[0m"
  232.     cat /tmp/ss | grep -E "$regSS" | grep SYN-RECV | awk -F'[: ]+’ ‘{sum[$(NF-2)]+=1}END{for (ip in sum){print ip,sum[ip]}}’ | sort -k 2 -nr | head -n 10
  233. }
  234.  
  235. main(){
  236.     while true; do
  237.         echo -e "1) real time traffic.n2) traffic and connection overview.n"
  238.         read -p "please input your select(ie 1): " select
  239.         case  $select in
  240.             1) realTimeTraffic;break;;
  241.             2) trafficAndConnectionOverview;break;;
  242.             *) echo "input error,please input a number.";;
  243.         esac
  244.     done   
  245. }
  246.  
  247. main

脚本中如有不明白的地方,可以留言咨询。

使用shell脚本结合innobackupex自动备份mysql innodb数据库

上篇文章介绍了使用Xtrabackup备份mysql innodb数据库,这次给出一个自动备份的shell脚本,脚本每周六作一次完整备份,其它时间每天作一次增量备份。

  1. #!/bin/bash
  2. #检查命令是否执行成功
  3. if_sucess(){
  4. local command="$1"
  5. $command
  6. if [ $? -ne 0 ];then
  7. echo "error."
  8. touch $error_lock_file
  9. exit 1
  10. fi
  11. }
  12. #检查是否存在锁文件,如果存在就退出。
  13. check_locked(){
  14. if [ -f  "$error_lock_file" ];then
  15. echo "error_lock_file found"
  16. exit 1
  17. fi
  18. }
  19.  
  20. #压缩备份一周的完整与增量备份
  21. compress_old_backup(){
  22. if_sucess "tar czf $old_backup_file_temp $full_backup_dir $incr_backup_dir"
  23. rm -f $old_backup_file
  24. if_sucess "mv $old_backup_file_temp $old_backup_file"
  25. rm -rf $full_backup_dir $incr_backup_dir/*
  26. }
  27. #定义相关变量
  28. backup_base=/data/mysql_backup
  29. full_backup_dir=$backup_base/centos_full_backup
  30. incr_backup_dir=$backup_base/centos_incr
  31. sub_incr_dir=$(date +%w)
  32. old_backup_file=$backup_base/old/centos_old.tar.gz
  33. old_backup_file_temp=$backup_base/old/centos_old_temp.tar.gz
  34. user=devops.webres.wang
  35. password=123456
  36. defaults_file=/etc/mysql/my.cnf
  37. include_db="centos.*|mysql.*"
  38. error_lock_file=$backup_base/error.locked
  39.  
  40. #程序从这里开始
  41. check_locked
  42. mkdir -p  $incr_backup_dir $backup_base/old
  43.  
  44. #周六就作完整备份,其它时间增量备份。
  45. if [ $sub_incr_dir -eq 6 ];then
  46. [ -d "$full_backup_dir" ] && compress_old_backup
  47. if_sucess "innobackupex –user=$user –password=$password  –defaults-file=$defaults_file –no-timestamp –include=$include_db  $full_backup_dir"
  48. echo "incr_base_dir=$full_backup_dir" > $full_backup_dir/incr_base_dir.txt
  49. else
  50. [ -f "$full_backup_dir/incr_base_dir.txt" ] && . $full_backup_dir/incr_base_dir.txt || exit 1
  51. if_sucess "innobackupex –user=$user –password=$password  –defaults-file=$defaults_file –incremental $incr_backup_dir/$sub_incr_dir –no-timestamp  –include=$include_db –incremental-basedir=$incr_base_dir"
  52. echo "$incr_base_dir=$incr_backup_dir/$sub_incr_dir" > $full_backup_dir/incr_base_dir.txt
  53. fi

Bash Shell字符串操作小结

1. 取长度

  1. str="abcd"
  2. expr length $str   # 4
  3. echo ${#str}       # 4
  4. expr "$str" : ".*" # 4

好像一般使用第二种

2. 查找子串的位置

  1. str="abc"
  2. expr index $str "a"  # 1
  3. expr index $str "b"  # 2
  4. expr index $str "x"  # 0
  5. expr index $str ""   # 0

3. 选取子串

  1. str="abcdef"
  2. expr substr "$str" 1 3  # 从第一个位置开始取3个字符, abc
  3. expr substr "$str" 2 5  # 从第二个位置开始取5个字符, bcdef
  4. expr substr "$str" 4 5  # 从第四个位置开始取5个字符, def
  5. echo ${str:2}           # 从第二个位置开始提取字符串, bcdef
  6. echo ${str:2:3}         # 从第二个位置开始提取3个字符, bcd
  7. echo ${str:(-6):5}        # 从倒数第二个位置向左提取字符串, abcde
  8. echo ${str:(-4):3}      # 从倒数第二个位置向左提取6个字符, cde

4. 截取子串

  1. str="abbc,def,ghi,abcjkl"
  2. echo ${str#a*c}     # 输出,def,ghi,abcjkl  一个井号(#) 表示从左边截取掉最短的匹配 (这里把abbc字串去掉)
  3. echo ${str##a*c}    # 输出jkl,             两个井号(##) 表示从左边截取掉最长的匹配 (这里把abbc,def,ghi,abc字串去掉)
  4. echo ${str#"a*c"}   # 输出abbc,def,ghi,abcjkl 因为str中没有"a*c"子串
  5. echo ${str##"a*c"}  # 输出abbc,def,ghi,abcjkl 同理
  6. echo ${str#*a*c*}   # 空
  7. echo ${str##*a*c*}  # 空
  8. echo ${str#d*f)     # 输出abbc,def,ghi,abcjkl,
  9. echo ${str#*d*f}    # 输出,ghi,abcjkl 
  10. echo ${str%a*l}     # abbc,def,ghi  一个百分号(%)表示从右边截取最短的匹配
  11. echo ${str%%b*l}    # a             两个百分号表示(%%)表示从右边截取最长的匹配
  12. echo ${str%a*c}     # abbc,def,ghi,abcjkl

可以这样记忆, 井号(#)通常用于表示一个数字,它是放在前面的;百分号(%)卸载数字的后面; 或者这样记忆,在键盘布局中,井号(#)总是位于百分号(%)的左边(即前面) 🙂

5. 字符串替换

  1. str="apple, tree, apple tree"
  2. echo ${str/apple/APPLE}   # 替换第一次出现的apple
  3. echo ${str//apple/APPLE}  # 替换所有apple
  4. echo ${str/#apple/APPLE}  # 如果字符串str以apple开头,则用APPLE替换它
  5. echo ${str/%apple/APPLE}  # 如果字符串str以apple结尾,则用APPLE替换它

6. 比较

  1. [[ "a.txt" == a* ]]        # 逻辑真 (pattern matching)
  2. [[ "a.txt" =~ .*.txt ]]   # 逻辑真 (regex matching)
  3. [[ "abc" == "abc" ]]       # 逻辑真 (string comparision)
  4. [[ "11" < "2" ]]           # 逻辑真 (string comparision), 按ascii值比较

7. 连接

  1. s1="hello"
  2. s2="world"
  3. echo ${s1}${s2}   # 当然这样写 $s1$s2 也行,但最好加上大括号

转自:http://my.oschina.net/aiguozhe/blog/41557

使用sed对nginx配置文件进行删除和列出虚拟主机操作

带着需要使用sed来对nginx配置文件进行操作的强烈需求,于是开始了学习sed的高级应用。虽然之前也一直在用sed,但也只是接触到了s替换命令,其它高级的命令没用到,所以没有动力去学。一直觉得要学到点东西,前提是你现在有一问题,需要用到这个技术来解决,而且有强烈的渴望要把这个问题解决,这时候你学习这项技术会事半功倍。否则学习起来会非常的枯燥无味,效率低,甚至会放弃。下面是我最近学sed得出的成果,备忘一下,以防失忆。
一、列出所有虚拟的server_name和对应的root

  1. sed -n "
  2. /servers*{/{
  3. H
  4. :loop
  5. n;H
  6. /}/{
  7. s/}//;x;H
  8. s/.*server_names*([^;]*);.*/1/p
  9. x
  10. s/.*roots*([^;]*);.*/1/p
  11. s/.*//g;x
  12. }
  13. /{/!b loop
  14. :loop1
  15. n;H
  16. /}/!b loop1
  17. b loop
  18. }
  19. " nginx-devops.webres.wang.conf

二、删除指定server_name的虚拟主机

  1. domain="devops.webres.wang"
  2. sed -i -n "
  3. /servers*{/{
  4. H
  5. :loop
  6. n;H
  7. /}/{
  8. s/}//;x
  9. /.*server_names*$domain.*/d
  10. p;d
  11. }
  12. /{/!b loop
  13. :loop1
  14. n;H
  15. /}/!b loop1
  16. b loop
  17. }
  18. p
  19. " nginx-devops.webres.wang.conf

代码有点多,就不具体分析了,具体说下如何找出server {} 代码块。

代码分析:
1、首先先关闭自动打印功能,即-n选项,因为下面会用到n命令,会自动打印,所以添加-n之后,使用n命令就不会自动打印了,需要打印时,使用p命令。
2、匹配 server {,如果匹配到,则把pattern space的内容添加到hold space,然后进入loop循环,执行n命令(n命令不必进入新的循环读取下一步到pattern space),再用H附加到hold space
3、判断是否找到代码块结束符},如果找到,进入server {}代码块处理阶段,处理完之后,进入下一个循环,继续找出下一个server{}。
4、如果找不到,再判断是否找到{,如果没有找到,进入loop循环
5、如果找到了{,表示存在if {},或者location {},则必要找到两个}才算是完整的server代码块。此时进入loop1循环,n命令读取下一行,附加到hold space。
6、再判断是否找到},如果没有找到,继续loop1循环,如果找到了,还需要一个},所以跳到loop循环。
7、如此循环下来,直接找到完整的server代码块。

关键的几点提示:
1、n命令是直接清空pattern space,并读取下一行到pattern space,而不需要进入新的循环。在这里的作用是,因为要把pattern space的内容附加到hold space,所以每读取一行,必需先清空pattern space,然后再附加到hold space,这样hold space才不会出现重复内容。要达到清空pattern space,读取下一行,进入下一循环可以做到,但进入下一循环后,必需重新匹配server {,而下一行肯定无法匹配,所以就达不到累积的效果。
2、hold space在这里很重要,它的内容来自pattern space对其进行附加操作。累积到完整的server代码块时,再与pattern space进行交换,交换之前必需把pattern space清空,这样交换后,hold space才能为空,为累积出下一个server代码块做准备。
最后还是建议使用sed的debug工具sedsed查看pattern space和hold space的变化。http://aurelio.net/projects/sedsed/

sed高级应用示例

最近需要使用sed来解析nginx配置文件,而之前使用sed仅限制于对文件的替换及添加文本,不过也基本能满足平时的bash shell脚本的编写工作。但这次需要解析nginx配置文件来对虚拟主机的代码块进行处理,比如对指定虚拟主机的删除,以及列出所有虚拟主机的信息,比如根目录是哪个。单靠简单的匹配是无法满足这个需求了,于是重读了一遍http://www.gnu.org/software/sed/manual/sed.htmlsed的教程,开始渐渐懂得sed的工作原理以及如何使用sed的高级功能。在分析高级应用的例子之前,我们来了解下sed的工作原理,这至关重要。

sed工作原理

sed是一个流文本处理工具,从文件头到文件尾读取,一次只读取一行,并完成一系列操作才继续读取下一行。sed维护两个数据缓冲区,一个是pattern space,一个是hold space。它们初始都为空。pattern space是活跃缓冲区,每一次循环都会清空再存入下一行内容。hold space一个辅助的空间,不会在完成一个循环后清空,会一直保持,它的内容来自使用h,H,g,G命令得来。

sed读取输入流的一行,在读取下一行之前,需要做如下操作(完成这些操作视为完成一个循环):sed从输入流读取一行,删除换行符,并把内容放到pattern space。然后命令开始对pattern space进行操作。每个命令可以有address关联,如/devops.webres.wang/a\hello,/devops.webres.wang/是搜索包括devops.webres.wang的pattern space,然后再执行a\hello增加hello字符操作,/devops.webres.wang/即为address,a为命令。只有address为真时,即匹配成功时,才执行后面的命令。

除非使用特殊的命令,如”D”,否则pattern space会在两个循环之间被清空。而hold space则会保持不变,hold space的内容可以使用‘h’, ‘H’, ‘x’, ‘g’, ‘G’的命令来操作。

高级应用示例分析

下面的例子来自http://www.gnu.org/software/sed/manual/sed.html
示例一
下面的脚本实现了每行80列宽中间对齐,假如文件中有aabb和ccccdddd两行。

  1. #!/usr/bin/sed -f
  2.     
  3.      # Put 80 spaces in the buffer
  4.      1 {
  5.        x
  6.        s/^$/          /
  7.        s/^.*$/&&&&&&&&/
  8.        x
  9.      }
  10.     
  11.      # del leading and trailing spaces
  12.      y/t/ /
  13.      s/^ *//
  14.      s/ *$//
  15.     
  16.      # add a newline and 80 spaces to end of line
  17.      G
  18.     
  19.      # keep first 81 chars (80 + a newline)
  20.      s/^(.{81}).*$/1/
  21.     
  22.      # 2 matches half of the spaces, which are moved to the beginning
  23.      s/^(.*)n(.*)2/21/

代码分析:

  1. 读取第一行时,pattern space为aabb,hold space为空。
  2. 以下命令分析:
  3. 1 {
  4.    x
  5.    s/^$/          /
  6.    s/^.*$/&&&&&&&&/
  7.    x
  8.    }
  9. 匹配第一行,执行如下命令,
  10. 执行x命令:交换pattern space和hold space的内容,结果是,pattern space内容为空,hold space为aabb。
  11. 执行s/^$/          /命令:pattern space为8个空格,hold space不变。
  12. 执行s/^.*$/&&&&&&&&/命令:现在pattern space的空格为80个,hold space不变。
  13. 执行x命令:交换它们的内容,pattern space内容为aabb,hold space为80个空格。
  14.  
  15. 继续执行如下命令:
  16. y/t/ /:替换tab为一个空格
  17. s/^ *//:删除行尾空格
  18. s/ *$//:删除行首空格
  19.  
  20. 执行G命令:pattern space附加一换行符,并附加hold space内容到pattern space,结果是,pattern space为aabb+n+80个空格,hold space保持不变。
  21.  
  22. s/^(.{81}).*$/1/命令:用s命令从行首至行尾取81个字符,包括了换行符。
  23.  
  24. s/^(.*)n(.*)2/21/命令:用正则把pattern space后面的空格分半,并移至行首,这样就实现了80列宽度中间对齐。
  25.  
  26. 继续下面的行读取时,hold space的内容会一直保持不变。

示例二
下面的例子实现了为数字加1的效果,比如一个文件number.txt,文件内容为:

  1. 6

sed代码:

  1. #!/usr/bin/sed -f
  2.     
  3.      /[^0-9]/ d
  4.     
  5.      # replace all leading 9s by _ (any other character except digits, could
  6.      # be used)
  7.      :d
  8.      s/9(_*)$/_1/
  9.      td
  10.     
  11.      # incr last digit only.  The first line adds a most-significant
  12.      # digit of 1 if we have to add a digit.
  13.      #
  14.      # The tn commands are not necessary, but make the thing
  15.      # faster
  16.     
  17.      s/^(_*)$/11/; tn
  18.      s/8(_*)$/91/; tn
  19.      s/7(_*)$/81/; tn
  20.      s/6(_*)$/71/; tn
  21.      s/5(_*)$/61/; tn
  22.      s/4(_*)$/51/; tn
  23.      s/3(_*)$/41/; tn
  24.      s/2(_*)$/31/; tn
  25.      s/1(_*)$/21/; tn
  26.      s/0(_*)$/11/; tn
  27.     
  28.      :n
  29.      y/_/0/

代码分析:

  1. 读取第一行6,放到pattern space,
  2. 执行/[^0-9]/ d:删除不是纯数字的行,pattern space为6
  3. 执行:d :标记下面的命令为子命令,用于跳转
  4. s/9(_*)$/_1/:替换9为_,此时pattern space 6不变。
  5. td :测试label d的子命令是否更改pattern space,如果更改,则跳回d标记处,否则继续往下执行。
  6.  
  7. s/^(_*)$/11/; tn
  8. s/8(_*)$/91/; tn
  9. s/7(_*)$/81/; tn
  10. 执行了以上命令,pattern space还是6
  11. s/6(_*)$/71/; tn
  12. 6替换成了7,此时pattern space为7
  13.  
  14. s/5(_*)$/61/; tn
  15. s/4(_*)$/51/; tn
  16. s/3(_*)$/41/; tn
  17. s/2(_*)$/31/; tn
  18. s/1(_*)$/21/; tn
  19. s/0(_*)$/11/; tn
  20. :n
  21. y/_/0/
  22. 执行以上几个命令,pattern space还是7,打印出7,继续下一循环。

示例三
此例子实现了每行倒序的效果。

  1. #!/usr/bin/sed -f
  2. /../! b
  3.  
  4. # Reverse a line.  Begin embedding the line between two newlines
  5. s/^.*$/n&n/
  6.  
  7. # Move first character at the end.  The regexp matches until
  8. # there are zero or one characters between the markers
  9. tx
  10. 😡
  11. s/(n.)(.*)(.n)/321/
  12. tx
  13.  
  14. # Remove the newline markers
  15. s/n//g

代码分析(以输入abcdef为例):

  1. 读取abcdef,
  2. pattern space为abcdef
  3. /../! b:如果只有一个字符,直接打印,中止此循环,进入下一循环。
  4.  
  5. s/^.*$/n&n/:用两个换行符包围abcdef,此刻pattern space为nabcdefn。
  6.  
  7. 😡
  8. s/(n.)(.*)(.n)/321/
  9. tx
  10.  
  11. 经过s命令后,1为na,2为bcde,3为fn,pattern space变为fnbcdena,此刻首尾字符换了位置。
  12. 执行tx命令,发现pattern space已经改变,跳转到x子命令,继续替换操作。
  13. 再次执行s命令后,换行符中间的字符又首尾调换了一次,pattern space为fencdnba
  14. 再一次s命令,pattern space为fednncba,
  15. 再一次s命令,pattern space不变,
  16. tx检测pattern space不变,于是往下执行,
  17. s/n//g:全局替换n,于是pattern space为fedcba,打印出fedcba。

示例四
下面的示例实现了以行为单位对文件进行倒序查看,相当于linux下的tac命令。

  1. #!/usr/bin/sed -nf
  2.  
  3. # reverse all lines of input, i.e. first line became last, …
  4.  
  5. # from the second line, the buffer (which contains all previous lines)
  6. # is *appended* to current line, so, the order will be reversed
  7. 1! G
  8.  
  9. # on the last line we’re done — print everything
  10. $ p
  11.  
  12. # store everything on the buffer again
  13. h

代码分析:
以文件内容为:
devops.webres.wang
www.baidu.com
www.qq.com
为例:

  1. 读取第一行:
  2. pattern space为devops.webres.wang
  3.  
  4. 1! G,如果不是第一行,执行G,附加一换行符到pattern space,再附加hold space内容到pattern space。此刻pattern space还是devops.webres.wang
  5.  
  6. $ p 如果到文件尾,则打印。
  7.  
  8. h命令:用pattern space内容替换hold space内容,现在pattern space和hold space都为devops.webres.wang。
  9.  
  10. 读取第二行
  11. pattern space为bb,hold space为devops.webres.wang
  12.  
  13. 1! G: pattern space为www.baidu.comndevops.webres.wang
  14. h命令:pattern space和hold space都为www.baidu.comndevops.webres.wang
  15.  
  16. 读取第三行
  17. pattern space为www.qq.com,hold space为www.baidu.comndevops.webres.wang
  18.  
  19. 1! G:pattern space为www.qq.comnwww.baidu.comndevops.webres.wang
  20. h命令:pattern space和hold space都为www.qq.comnwww.baidu.comndevops.webres.wang
  21.  
  22. $ p:已到文件尾,执行打印paatern space操作,结果为:
  23. www.qq.com
  24. www.baidu.com
  25. devops.webres.wang

gnu关于sed教程还有很多示例涉及到sed的高级功能,时间有限,先分析到这里。有空再继续。下篇日志贴出并分析使用sed解析nginx配置文件中的server {}代码块,实现列出虚拟主机的server_name root等信息,以及指定server_name的虚拟主机删除操作。
最后介绍一个很好用的sed debug工具,它可以显示出所有pattern space和hold space实时状态。http://aurelio.net/projects/sedsed/

shell脚本制作俄罗斯方块游戏

下面的脚本功能强大,make by xhchen,收藏一下。

 

  1. #!/bin/bash
  2.  
  3. # Tetris Game
  4. # 10.21.2003 xhchen<[email]xhchen@winbond.com.tw[/email]>
  5.  
  6. #APP declaration
  7. APP_NAME="${0##*[\/]}"
  8. APP_VERSION="1.0"
  9.  
  10.  
  11. #颜色定义
  12. cRed=1
  13. cGreen=2
  14. cYellow=3
  15. cBlue=4
  16. cFuchsia=5
  17. cCyan=6
  18. cWhite=7
  19. colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite)
  20.  
  21. #位置和大小
  22. iLeft=3
  23. iTop=2
  24. ((iTrayLeft = iLeft + 2))
  25. ((iTrayTop = iTop + 1))
  26. ((iTrayWidth = 10))
  27. ((iTrayHeight = 15))
  28.  
  29. #颜色设置
  30. cBorder=$cGreen
  31. cScore=$cFuchsia
  32. cScoreValue=$cCyan
  33.  
  34. #控制信号
  35. #改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
  36. #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
  37. sigRotate=25
  38. sigLeft=26
  39. sigRight=27
  40. sigDown=28
  41. sigAllDown=29
  42. sigExit=30
  43.  
  44. #七中不同的方块的定义
  45. #通过旋转,每种方块的显示的样式可能有几种
  46. box0=(0 0 0 1 1 0 1 1)
  47. box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
  48. box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0)
  49. box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1)
  50. box4=(0 1 0 2 1 1 2 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 0 1 0 1 1 1 2)
  51. box5=(0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 0 1 1 1 2)
  52. box6=(0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 1 0 1 1 1 2)
  53. #所有其中方块的定义都放到box变量中
  54. box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
  55. #各种方块旋转后可能的样式数目
  56. countBox=(1 2 2 2 4 4 4)
  57. #各种方块再box数组中的偏移
  58. offsetBox=(0 1 3 5 7 11 15)
  59.  
  60. #每提高一个速度级需要积累的分数
  61. iScoreEachLevel=50        #be greater than 7
  62.  
  63. #运行时数据
  64. sig=0                #接收到的signal
  65. iScore=0        #总分
  66. iLevel=0        #速度级
  67. boxNew=()        #新下落的方块的位置定义
  68. cBoxNew=0        #新下落的方块的颜色
  69. iBoxNewType=0        #新下落的方块的种类
  70. iBoxNewRotate=0        #新下落的方块的旋转角度
  71. boxCur=()        #当前方块的位置定义
  72. cBoxCur=0        #当前方块的颜色
  73. iBoxCurType=0        #当前方块的种类
  74. iBoxCurRotate=0        #当前方块的旋转角度
  75. boxCurX=-1        #当前方块的x坐标位置
  76. boxCurY=-1        #当前方块的y坐标位置
  77. iMap=()                #背景方块图表
  78.  
  79. #初始化所有背景方块为-1, 表示没有方块
  80. for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done
  81.  
  82.  
  83. #接收输入的进程的主函数
  84. function RunAsKeyReceiver()
  85. {
  86.         local pidDisplayer key aKey sig cESC sTTY
  87.  
  88.         pidDisplayer=$1
  89.         aKey=(0 0 0)
  90.  
  91.         cESC=`echo -ne "33"`
  92.         cSpace=`echo -ne "40"`
  93.  
  94.         #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
  95.         #如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
  96.         #需要在程序退出时恢复终端属性。
  97.         sTTY=`stty -g`
  98.  
  99.         #捕捉退出信号
  100.         trap "MyExit;" INT TERM
  101.         trap "MyExitNoSub;" $sigExit
  102.  
  103.         #隐藏光标
  104.         echo -ne "33[?25l"
  105.  
  106.  
  107.         while :
  108.         do
  109.                 #读取输入。注-s不回显,-n读到一个字符立即返回
  110.                 read -s -n 1 key
  111.  
  112.                 aKey[0]=${aKey[1]}
  113.                 aKey[1]=${aKey[2]}
  114.                 aKey[2]=$key
  115.                 sig=0
  116.  
  117.                 #判断输入了何种键
  118.                 if [[ $key == $cESC && ${aKey[1]} == $cESC ]]
  119.                 then
  120.                         #ESC键
  121.                         MyExit
  122.                 elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]]
  123.                 then
  124.                         if [[ $key == "A" ]]; then sig=$sigRotate        #<向上键>
  125.                         elif [[ $key == "B" ]]; then sig=$sigDown        #<向下键>
  126.                         elif [[ $key == "D" ]]; then sig=$sigLeft        #<向左键>
  127.                         elif [[ $key == "C" ]]; then sig=$sigRight        #<向右键>
  128.                         fi
  129.                 elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate        #W, w
  130.                 elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown        #S, s
  131.                 elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft        #A, a
  132.                 elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight        #D, d
  133.                 elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown        #空格键
  134.                 elif [[ $key == "Q" || $key == "q" ]]                        #Q, q
  135.                 then
  136.                         MyExit
  137.                 fi
  138.  
  139.                 if [[ $sig != 0 ]]
  140.                 then
  141.                         #向另一进程发送消息
  142.                         kill -$sig $pidDisplayer
  143.                 fi
  144.         done
  145. }
  146.  
  147. #退出前的恢复
  148. function MyExitNoSub()
  149. {
  150.         local y
  151.  
  152.         #恢复终端属性
  153.         stty $sTTY
  154.         ((y = iTop + iTrayHeight + 4))
  155.  
  156.         #显示光标
  157.         echo -e "33[?25h33[${y};0H"
  158.         exit
  159. }
  160.  
  161.  
  162. function MyExit()
  163. {
  164.         #通知显示进程需要退出
  165.         kill -$sigExit $pidDisplayer
  166.  
  167.         MyExitNoSub
  168. }
  169.  
  170.  
  171. #处理显示和游戏流程的主函数
  172. function RunAsDisplayer()
  173. {
  174.         local sigThis
  175.         InitDraw
  176.  
  177.         #挂载各种信号的处理函数
  178.         trap "sig=$sigRotate;" $sigRotate
  179.         trap "sig=$sigLeft;" $sigLeft
  180.         trap "sig=$sigRight;" $sigRight
  181.         trap "sig=$sigDown;" $sigDown
  182.         trap "sig=$sigAllDown;" $sigAllDown
  183.         trap "ShowExit;" $sigExit
  184.  
  185.         while :
  186.         do
  187.                 #根据当前的速度级iLevel不同,设定相应的循环的次数
  188.                 for ((i = 0; i < 21 – iLevel; i++))
  189.                 do
  190.                         sleep 0.02
  191.                         sigThis=$sig
  192.                         sig=0
  193.  
  194.                         #根据sig变量判断是否接受到相应的信号
  195.                         if ((sigThis == sigRotate)); then BoxRotate;        #旋转
  196.                         elif ((sigThis == sigLeft)); then BoxLeft;        #左移一列
  197.                         elif ((sigThis == sigRight)); then BoxRight;        #右移一列
  198.                         elif ((sigThis == sigDown)); then BoxDown;        #下落一行
  199.                         elif ((sigThis == sigAllDown)); then BoxAllDown;        #下落到底
  200.                         fi
  201.                 done
  202.                 #kill -$sigDown $$
  203.                 BoxDown        #下落一行
  204.         done
  205. }
  206.  
  207.  
  208. #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
  209. function BoxMove()
  210. {
  211.         local j i x y xTest yTest
  212.         yTest=$1
  213.         xTest=$2
  214.         for ((j = 0; j < 8; j += 2))
  215.         do
  216.                 ((i = j + 1))
  217.                 ((y = ${boxCur[$j]} + yTest))
  218.                 ((x = ${boxCur[$i]} + xTest))
  219.                 if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth))
  220.                 then
  221.                         #撞到墙壁了
  222.                         return 1
  223.                 fi
  224.                 if ((${iMap[y * iTrayWidth + x]} != -1 ))
  225.                 then
  226.                         #撞到其他已经存在的方块了
  227.                         return 1
  228.                 fi
  229.         done
  230.         return 0;
  231. }
  232.  
  233.  
  234. #将当前移动中的方块放到背景方块中去,
  235. #并计算新的分数和速度级。(即一次方块落到底部)
  236. function Box2Map()
  237. {
  238.         local j i x y xp yp line
  239.  
  240.         #将当前移动中的方块放到背景方块中去
  241.         for ((j = 0; j < 8; j += 2))
  242.         do
  243.                 ((i = j + 1))
  244.                 ((y = ${boxCur[$j]} + boxCurY))
  245.                 ((x = ${boxCur[$i]} + boxCurX))
  246.                 ((i = y * iTrayWidth + x))
  247.                 iMap[$i]=$cBoxCur
  248.         done
  249.  
  250.         #消去可被消去的行
  251.         line=0
  252.         for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
  253.         do
  254.                 for ((i = j + iTrayWidth – 1; i >= j; i–))
  255.                 do
  256.                         if ((${iMap[$i]} == -1)); then break; fi
  257.                 done
  258.                 if ((i >= j)); then continue; fi
  259.  
  260.                 ((line++))
  261.                 for ((i = j – 1; i >= 0; i–))
  262.                 do
  263.                         ((x = i + iTrayWidth))
  264.                         iMap[$x]=${iMap[$i]}
  265.                 done
  266.                 for ((i = 0; i < iTrayWidth; i++))
  267.                 do
  268.                         iMap[$i]=-1
  269.                 done
  270.         done
  271.  
  272.         if ((line == 0)); then return; fi
  273.  
  274.         #根据消去的行数line计算分数和速度级
  275.         ((x = iLeft + iTrayWidth * 2 + 7))
  276.         ((y = iTop + 11))
  277.         ((iScore += line * 2 – 1))
  278.         #显示新的分数
  279.         echo -ne "33[1m33[3${cScoreValue}m33[${y};${x}H${iScore}         "
  280.         if ((iScore % iScoreEachLevel < line * 2 – 1))
  281.         then
  282.                 if ((iLevel < 20))
  283.                 then
  284.                         ((iLevel++))
  285.                         ((y = iTop + 14))
  286.                         #显示新的速度级
  287.                         echo -ne "33[3${cScoreValue}m33[${y};${x}H${iLevel}        "
  288.                 fi
  289.         fi
  290.         echo -ne "33[0m"
  291.  
  292.  
  293.         #重新显示背景方块
  294.         for ((y = 0; y < iTrayHeight; y++))
  295.         do
  296.                 ((yp = y + iTrayTop + 1))
  297.                 ((xp = iTrayLeft + 1))
  298.                 ((i = y * iTrayWidth))
  299.                 echo -ne "33[${yp};${xp}H"
  300.                 for ((x = 0; x < iTrayWidth; x++))
  301.                 do
  302.                         ((j = i + x))
  303.                         if ((${iMap[$j]} == -1))
  304.                         then
  305.                                 echo -ne "  "
  306.                         else
  307.                                 echo -ne "33[1m33[7m33[3${iMap[$j]}m33[4${iMap[$j]}m[]33[0m"
  308.                         fi
  309.                 done
  310.         done
  311. }
  312.  
  313.  
  314. #下落一行
  315. function BoxDown()
  316. {
  317.         local y s
  318.         ((y = boxCurY + 1))        #新的y坐标
  319.         if BoxMove $y $boxCurX        #测试是否可以下落一行
  320.         then
  321.                 s="`DrawCurBox 0`"        #将旧的方块抹去
  322.                 ((boxCurY = y))
  323.                 s="$s`DrawCurBox 1`"        #显示新的下落后方块
  324.                 echo -ne $s
  325.         else
  326.                 #走到这儿, 如果不能下落了
  327.                 Box2Map                #将当前移动中的方块贴到背景方块中
  328.                 RandomBox        #产生新的方块
  329.         fi
  330. }
  331.  
  332. #左移一列
  333. function BoxLeft()
  334. {
  335.         local x s
  336.         ((x = boxCurX – 1))
  337.         if BoxMove $boxCurY $x
  338.         then
  339.                 s=`DrawCurBox 0`
  340.                 ((boxCurX = x))
  341.                 s=$s`DrawCurBox 1`
  342.                 echo -ne $s
  343.         fi
  344. }
  345.  
  346. #右移一列
  347. function BoxRight()
  348. {
  349.         local x s
  350.         ((x = boxCurX + 1))
  351.         if BoxMove $boxCurY $x
  352.         then
  353.                 s=`DrawCurBox 0`
  354.                 ((boxCurX = x))
  355.                 s=$s`DrawCurBox 1`
  356.                 echo -ne $s
  357.         fi
  358. }
  359.  
  360.  
  361. #下落到底
  362. function BoxAllDown()
  363. {
  364.         local k j i x y iDown s
  365.         iDown=$iTrayHeight
  366.  
  367.         #计算一共需要下落多少行
  368.         for ((j = 0; j < 8; j += 2))
  369.         do
  370.                 ((i = j + 1))
  371.                 ((y = ${boxCur[$j]} + boxCurY))
  372.                 ((x = ${boxCur[$i]} + boxCurX))
  373.                 for ((k = y + 1; k < iTrayHeight; k++))
  374.                 do
  375.                         ((i = k * iTrayWidth + x))
  376.                         if (( ${iMap[$i]} != -1)); then break; fi
  377.                 done
  378.                 ((k -= y + 1))
  379.                 if (( $iDown > $k )); then iDown=$k; fi
  380.         done
  381.  
  382.         s=`DrawCurBox 0`        #将旧的方块抹去
  383.         ((boxCurY += iDown))
  384.         s=$s`DrawCurBox 1`        #显示新的下落后的方块
  385.         echo -ne $s
  386.         Box2Map                #将当前移动中的方块贴到背景方块中
  387.         RandomBox        #产生新的方块
  388. }
  389.  
  390.  
  391. #旋转方块
  392. function BoxRotate()
  393. {
  394.         local iCount iTestRotate boxTest j i s
  395.         iCount=${countBox[$iBoxCurType]}        #当前的方块经旋转可以产生的样式的数目
  396.  
  397.         #计算旋转后的新的样式
  398.         ((iTestRotate = iBoxCurRotate + 1))
  399.         if ((iTestRotate >= iCount))
  400.         then
  401.                 ((iTestRotate = 0))
  402.         fi
  403.  
  404.         #更新到新的样式, 保存老的样式(但不显示)
  405.         for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  406.         do
  407.                 boxTest[$j]=${boxCur[$j]}
  408.                 boxCur[$j]=${box[$i]}
  409.         done
  410.  
  411.         if BoxMove $boxCurY $boxCurX        #测试旋转后是否有空间放的下
  412.         then
  413.                 #抹去旧的方块
  414.                 for ((j = 0; j < 8; j++))
  415.                 do
  416.                         boxCur[$j]=${boxTest[$j]}
  417.                 done
  418.                 s=`DrawCurBox 0`
  419.  
  420.                 #画上新的方块
  421.                 for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  422.                 do
  423.                         boxCur[$j]=${box[$i]}
  424.                 done
  425.                 s=$s`DrawCurBox 1`
  426.                 echo -ne $s
  427.                 iBoxCurRotate=$iTestRotate
  428.         else
  429.                 #不能旋转,还是继续使用老的样式
  430.                 for ((j = 0; j < 8; j++))
  431.                 do
  432.                         boxCur[$j]=${boxTest[$j]}
  433.                 done
  434.         fi
  435. }
  436.  
  437.  
  438. #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
  439. function DrawCurBox()
  440. {
  441.         local i j t bDraw sBox s
  442.         bDraw=$1
  443.  
  444.         s=""
  445.         if (( bDraw == 0 ))
  446.         then
  447.                 sBox="4040"
  448.         else
  449.                 sBox="[]"
  450.                 s=$s"33[1m33[7m33[3${cBoxCur}m33[4${cBoxCur}m"
  451.         fi
  452.  
  453.         for ((j = 0; j < 8; j += 2))
  454.         do
  455.                 ((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY))
  456.                 ((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]})))
  457.                 #33[y;xH, 光标到(x, y)处
  458.                 s=$s"33[${i};${t}H${sBox}"
  459.         done
  460.         s=$s"33[0m"
  461.         echo -n $s
  462. }
  463.  
  464.  
  465. #更新新的方块
  466. function RandomBox()
  467. {
  468.         local i j t
  469.  
  470.         #更新当前移动的方块
  471.         iBoxCurType=${iBoxNewType}
  472.         iBoxCurRotate=${iBoxNewRotate}
  473.         cBoxCur=${cBoxNew}
  474.         for ((j = 0; j < ${#boxNew[@]}; j++))
  475.         do
  476.                 boxCur[$j]=${boxNew[$j]}
  477.         done
  478.  
  479.  
  480.         #显示当前移动的方块
  481.         if (( ${#boxCur[@]} == 8 ))
  482.         then
  483.                 #计算当前方块该从顶端哪一行"冒"出来
  484.                 for ((j = 0, t = 4; j < 8; j += 2))
  485.                 do
  486.                         if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  487.                 done
  488.                 ((boxCurY = -t))
  489.                 for ((j = 1, i = -4, t = 20; j < 8; j += 2))
  490.                 do
  491.                         if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi
  492.                         if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  493.                 done
  494.                 ((boxCurX = (iTrayWidth – 1 – i – t) / 2))
  495.  
  496.                 #显示当前移动的方块
  497.                 echo -ne `DrawCurBox 1`
  498.  
  499.                 #如果方块一出来就没处放,Game over!
  500.                 if ! BoxMove $boxCurY $boxCurX
  501.                 then
  502.                         kill -$sigExit ${PPID}
  503.                         ShowExit
  504.                 fi
  505.         fi
  506.  
  507.  
  508.  
  509.         #清除右边预显示的方块
  510.         for ((j = 0; j < 4; j++))
  511.         do
  512.                 ((i = iTop + 1 + j))
  513.                 ((t = iLeft + 2 * iTrayWidth + 7))
  514.                 echo -ne "33[${i};${t}H        "
  515.         done
  516.  
  517.         #随机产生新的方块
  518.         ((iBoxNewType = RANDOM % ${#offsetBox[@]}))
  519.         ((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]}))
  520.         for ((j = 0, i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; j++, i++))
  521.         do
  522.                 boxNew[$j]=${box[$i]};
  523.         done
  524.  
  525.         ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]}))
  526.  
  527.         #显示右边预显示的方块
  528.         echo -ne "33[1m33[7m33[3${cBoxNew}m33[4${cBoxNew}m"
  529.         for ((j = 0; j < 8; j += 2))
  530.         do
  531.                 ((i = iTop + 1 + ${boxNew[$j]}))
  532.                 ((t = iLeft + 2 * iTrayWidth + 7 + 2 * ${boxNew[$j + 1]}))
  533.                 echo -ne "33[${i};${t}H[]"
  534.         done
  535.         echo -ne "33[0m"
  536. }
  537.  
  538.  
  539. #初始绘制
  540. function InitDraw()
  541. {
  542.         clear
  543.         RandomBox        #随机产生方块,这时右边预显示窗口中有方快了
  544.         RandomBox        #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
  545.         local i t1 t2 t3
  546.  
  547.         #显示边框
  548.         echo -ne "33[1m"
  549.         echo -ne "33[3${cBorder}m33[4${cBorder}m"
  550.  
  551.         ((t2 = iLeft + 1))
  552.         ((t3 = iLeft + iTrayWidth * 2 + 3))
  553.         for ((i = 0; i < iTrayHeight; i++))
  554.         do
  555.                 ((t1 = i + iTop + 2))
  556.                 echo -ne "33[${t1};${t2}H||"
  557.                 echo -ne "33[${t1};${t3}H||"
  558.         done
  559.  
  560.         ((t2 = iTop + iTrayHeight + 2))
  561.         for ((i = 0; i < iTrayWidth + 2; i++))
  562.         do
  563.                 ((t1 = i * 2 + iLeft + 1))
  564.                 echo -ne "33[${iTrayTop};${t1}H=="
  565.                 echo -ne "33[${t2};${t1}H=="
  566.         done
  567.         echo -ne "33[0m"
  568.  
  569.  
  570.         #显示"Score"和"Level"字样
  571.         echo -ne "33[1m"
  572.         ((t1 = iLeft + iTrayWidth * 2 + 7))
  573.         ((t2 = iTop + 10))
  574.         echo -ne "33[3${cScore}m33[${t2};${t1}HScore"
  575.         ((t2 = iTop + 11))
  576.         echo -ne "33[3${cScoreValue}m33[${t2};${t1}H${iScore}"
  577.         ((t2 = iTop + 13))
  578.         echo -ne "33[3${cScore}m33[${t2};${t1}HLevel"
  579.         ((t2 = iTop + 14))
  580.         echo -ne "33[3${cScoreValue}m33[${t2};${t1}H${iLevel}"
  581.         echo -ne "33[0m"
  582. }
  583.  
  584.  
  585. #退出时显示GameOVer!
  586. function ShowExit()
  587. {
  588.         local y
  589.         ((y = iTrayHeight + iTrayTop + 3))
  590.         echo -e "33[${y};0HGameOver!33[0m"
  591.         exit
  592. }
  593.  
  594.  
  595. #显示用法.
  596. function Usage
  597. {
  598.         cat << EOF
  599. Usage: $APP_NAME
  600. Start tetris game.
  601.  
  602.   -h, –help              display this help and exit
  603.       –version           output version information and exit
  604. EOF
  605. }
  606.  
  607.  
  608. #游戏主程序在这儿开始.
  609. if [[ "$1" == "-h" || "$1" == "–help" ]]; then
  610.         Usage
  611. elif [[ "$1" == "–version" ]]; then
  612.         echo "$APP_NAME $APP_VERSION"
  613. elif [[ "$1" == "–show" ]]; then
  614.         #当发现具有参数–show时,运行显示函数
  615.         RunAsDisplayer
  616. else
  617.         bash $0 –show&        #以参数–show将本程序再运行一遍
  618.         RunAsKeyReceiver $!        #以上一行产生的进程的进程号作为参数
  619. fi

 

如何在Linux终端里用Shell和C输出带颜色的文字

我们知道,使用ls命令列出文件列表时,不同的文件类型会用不同的颜色显示。那么如何实现这样带颜色的文本输出呢?答案并不复杂,不管是用shell还是C语言。

一、shell下的实现方法

先来讲在shell下,如何实现。用echo命令就可以实现,参看以下例子:

echo -e “\033[32mHello, world!”

当你在终端里敲下这条命令后,是不是发现系统用绿色输出了”Hello,world!”,不止如此,连之后的命令提示符都变成了绿色?不要着急,听我继续说。echo命令-e选项的作用是激活终端对反斜线转义符(即\)的解释。引号内\033用于引导非常规字符序列,在这里的作用就是引导设置输出属性,后边的[32m就是将前景色设置为绿色,字母m表示设置的属性类别,数字代表属性值。设置可以单独使用,例如:

echo -e “\033[0m”

这行命令的作用是恢复属性为默认值,也就是说0m设置项用于恢复默认值。现在你的终端是不是又一切正常了?

理解了这些,剩下的就简单了。用这种命令,除了设置文本前景色,还可以设置很多属性。下边列出其他的设置项:

————————————————————————–

\033[0m 关闭所有属性
\033[1m 设置高亮度
\033[4m 下划线
\033[5m 闪烁
\033[7m 反显
\033[8m 消隐
\033[30m 至 \33[37m 设置前景色
\033[40m 至 \33[47m 设置背景色
\033[nA 光标上移n行
\033[nB 光标下移n行
\033[nC 光标右移n行
\033[nD 光标左移n行
\033[y;xH设置光标位置
\033[2J 清屏
\033[K 清除从光标到行尾的内容
\033[s 保存光标位置
\033[u 恢复光标位置
\033[?25l 隐藏光标
\033[?25h 显示光标

————————————————————————–

各数字所代表的颜色如下:

字背景颜色范围:40—-49
40:黑
41:深红
42:绿
43:黄色
44:蓝色
45:紫色
46:深绿
47:白色

字颜色:30———–39
30:黑
31:红
32:绿
33:黄
34:蓝色
35:紫色
36:深绿
37:白色

另外,同类的多种设置项可以组合在一起,中间用分号(;)隔开。如下:

echo -e “\033[20;1H\033[1;4;32mHello,world\033[0m”

这行命令首先\033[20;1H将光标移动到终端第20行第1列,之后的\033[1;4;32m将文本属性设置为高亮、带下划线且颜色为绿色,然后输出Hello,world;最后\033[0m将终端属性恢复为默认值,这样就不会看到连命令完成后的命令提示符也变了样儿了。

通过以上各种命令的组合就可以实现对终端输出地复杂控制。

二、如何在C编程中实现?

理解了以上在Shell中的实现方法,关于在C中如何实现就很简单了。可以说只需要用printf函数代替上边的echo -e就OK了。参见下例:

int color = 32;

printf(“\033[20;1H\033[1;4;%dmHello, world.\033[0m”, color);

这个例子类似上边shell中最后那个例子,只是这里颜色值通过变量color来指定(当然,也可以直接指定)。

三、联想

看到这里你可能会想,是不是在其他编程语言里也可以用类似的方法实现对终端输出的控制呢?答案是肯定的!比如在python中,可以如下输出:

color=32

print “\033[20;1H\033[1;4;%dHello, world.\033[0m”%color

这个例子的效果跟上边C的例子是相同的。
转自:http://blog.csdn.net/acmee/article/details/6613060

shell采集系统cpu 内存 磁盘 网络信息

cpu信息采集


cpu使用率

采集算法
通过/proc/stat文件采集并计算CPU总使用率或者单个核使用率。以cpu0为例,算法如下:
1. cat /proc/stat | grep ‘cpu0’得到cpu0的信息
2. cpuTotal1=user+nice+system+idle+iowait+irq+softirq
3. cpuUsed1=user+nice+system+irq+softirq
4. sleep 30秒
5. 再次cat /proc/stat | grep ‘cpu0’ 得到cpu的信息
6. cpuTotal2=user+nice+system+idle+iowait+irq+softirq
7. cpuUsed2=user+nice+system+irq+softirq
8. 得到cpu0 在30秒内的单核利用率:(cpuUsed2 – cpuUsed1) * 100 / (cpuTotal2 – cpuTotal1)
相当于使用top –d 30命令,把user、nice、system、irq、softirq五项的使用率相加。
shell代码:

  1. a=(`cat /proc/stat | grep -E "cpub" | awk -v total=0 ‘{$1="";for(i=2;i<=NF;i++){total+=$i};used=$2+$3+$4+$7+$8 }END{print total,used}’`)
  2. sleep 30
  3. b=(`cat /proc/stat | grep -E "cpub" | awk -v total=0 ‘{$1="";for(i=2;i<=NF;i++){total+=$i};used=$2+$3+$4+$7+$8 }END{print total,used}’`)
  4. cpu_usage=(((${b[1]}-${a[1]})*100/(${b[0]}-${a[0]})))

cpu负载

采集算法:
读取/proc/loadavg得到机器的1/5/15分钟平均负载,再乘以100。
shell代码:

  1. cpuload=(`cat /proc/loadavg | awk ‘{print $1,$2,$3}’`)
  2. load1=${cpuload[0]}
  3. load5=${cpuload[1]}
  4. load15=${cpuload[2]}

内存采集


应用程序使用内存

采集算法:
读取/proc/meminfo文件,(MemTotal – MemFree – Buffers – Cached)/1024得到应用程序使用内存数。
shell代码:

  1. awk ‘/MemTotal/{total=$2}/MemFree/{free=$2}/Buffers/{buffers=$2}/^Cached/{cached=$2}END{print (total-free-buffers-cached)/1024}’  /proc/meminfo

MEM使用量

采集算法:
读取/proc/meminfo文件,MemTotal – MemFree得到MEM使用量。
shell代码:

  1. awk ‘/MemTotal/{total=$2}/MemFree/{free=$2}END{print (total-free)/1024}’  /proc/meminfo

SWAP使用大小

采集算法:
通过/proc/meminfo文件,SwapTotal – SwapFree得到SWAP使用大小。
shell代码:

  1. awk ‘/SwapTotal/{total=$2}/SwapFree/{free=$2}END{print (total-free)/1024}’  /proc/meminfo

磁盘信息采集


disk io

1、IN:平均每秒把数据从硬盘读到物理内存的数据量
采集算法:
读取/proc/vmstat文件得出最近240秒内pgpgin的增量,把pgpgin的增量再除以240得到每秒的平均增量。
相当于vmstat 240命令bi一列的输出。
shell代码:

  1. a=`awk ‘/pgpgin/{print $2}’ /proc/vmstat`
  2. sleep 240
  3. b=`awk ‘/pgpgin/{print $2}’ /proc/vmstat`
  4. ioin=$(((b-a)/240))

2、OUT:平均每秒把数据从物理内存写到硬盘的数据量
采集算法:
读取/proc/vmstat文件得出最近240秒内pgpgout的增量,把pgpgout的增量再除以240得到每秒的平均增量。
相当于vmstat 240命令bo一列的输出。
shell代码:

  1. a=`awk ‘/pgpgout/{print $2}’ /proc/vmstat`
  2. sleep 240
  3. b=`awk ‘/pgpgout/{print $2}’ /proc/vmstat`
  4. ioout=$(((b-a)/240))

网络


流量

以http://devops.webres.wang/为例,eth0是内网,eth1外网,获取60秒的流量。
机器网卡的平均每秒流量
采集算法:
读取/proc/net/dev文件,得到60秒内发送和接收的字节数(KB),然后乘以8,再除以60,得到每秒的平均流量。
shell代码:

  1. traffic_be=(`awk -F'[: ]+’ ‘BEGIN{ORS=" "}/eth0/{print $3,$10}/eth1/{print $3,$11}’ /proc/net/dev`)
  2. sleep 60
  3. traffic_af=(`awk -F'[: ]+’ ‘BEGIN{ORS=" "}/eth0/{print $3,$10}/eth1/{print $3,$11}’ /proc/net/dev`)
  4. eth0_in=$(( (${traffic_af[0]}-${traffic_be[0]})/60 ))
  5. eth0_out=$(( (${traffic_af[1]}-${traffic_be[1]})/60 ))
  6. eth1_in=$(( (${traffic_af[2]}-${traffic_be[2]})/60 ))
  7. eth1_out=$(( (${traffic_af[3]}-${traffic_be[3]})/60 ))

包量

机器网卡的平均每秒包量
采集算法:
读取/proc/net/dev文件,得到60秒内发送和接收的包量,然后除以60,得到每秒的平均包量。
shell代码:

  1. packet_be=(`awk -F'[: ]+’ ‘BEGIN{ORS=" "}/eth0/{print $4,$12}/eth1/{print $4,$12}’ /proc/net/dev`)
  2. sleep 60
  3. packet_af=(`awk -F'[: ]+’ ‘BEGIN{ORS=" "}/eth0/{print $4,$12}/eth1/{print $4,$12}’ /proc/net/dev`)
  4. eth0_in=$(( (${packet_af[0]}-${packet_be[0]})/60 ))
  5. eth0_out=$(( (${packet_af[1]}- ${packet_be[1]})/60 ))
  6. eth1_in=$(( (${packet_af[2]}- ${packet_be[2]})/60 ))
  7. eth1_out=$(( (${packet_af[3]}- ${packet_be[3]})/60 ))