[Shell]服务器信息收集与整理输出wiki和excel

作者:Ajian 发布时间:August 28, 2012 分类:Shell

   前言:服务器信息的收集是认识你的工作环境的第一步,有了服务器的相关信息就知道如何去分配他们,更好的取舍,发挥每一台服务器的优点。之前一直有不同服务器信息收集脚本,最近又做了一次修改,主要采用awk,减少了各种管道和中间的过程,并且尽量的可以智能匹配,还有update wiki(redmine)和excel和输出更好的进行整理和对比。

Notice:由于脚本修改比较快,很多awk里面的变量没有什么规范,有时间再去整理吧,如果你对awk使用很熟练的话不会有什么问题的,真心发现awk越用越简单,使用了很多awk的特性,也发现awk不同版本造成的问题,像脚本中有一段是awk 4 支持关联数组 而awk 3不支持。就当是一次awk的脚本练习。

PS:最早期的脚本可见 http://www.ohlinux.com/archives/331/ 

服务器信息收集脚本 

ServerInfo_wiki_v2.sh

#!/bin/bash 
LANG=C

echo -e "{{>toc}} \n"

if [ ! `which dmidecode` ] || [ ! -e /usr/sbin/dmidecode ] ;then
if [ `which yum`];then yum -y install dmidecode > /dev/null 2>&1
else
echo "Please install dmidecode at first!! Exit."
exit
fi fi #OS Information if [ -e /etc/slackware-version ];then OS=`cat /etc/slackware-version | head -1` elif [ -e /etc/debian_version ]; then OS="Debian `cat /etc/debian_version`" elif [ -e /etc/redhat-release ]; then OS=`cat /etc/redhat-release` elif [ -e /etc/SuSE-release ]; then OS=`cat /etc/SuSE-release |head -n1` elif [ -e /etc/gentoo-release ]; then OS=`< /etc/gentoo-release` elif [ -e /etc/issue ]; then OS=`cat /etc/issue | head -1` elif [ -z $OS ] ;then OS='unknown' fi echo " h2. Base Infomation |_. Hostname | `/bin/hostname`|" #network Information /sbin/ifconfig -a | awk 'BEGIN{ in_section=0 end=1 }{ if (!end){ if (in_section){ if ( $0 ~ /^$/ ){ end=1 }else{ if ($0 ~ /inet addr:/){ split($2,ip,":") net=net""nic":"ip[2]" " } } } }else if (!($0 ~ /^ / || /lo/)){ end=0 in_section=1 nic=$1 } }END{ print "|_. Network |",net,"|" }' echo "|_. System kernel | `/bin/uname -sr`| |_. OS Name | $OS | |_. OS bit| `/bin/uname -m`|" #CPU Information cat /proc/cpuinfo | awk -F\: \ -v systembit=`/bin/uname -m` \ '{ if ($0 ~ /processor/){ cpucore++ } if ($0 ~ /model name/){ cputype=$2 } if ($0 ~ /physical id/){ cpuphy[$2]++ } if ($0 ~ /flags/){ if ($2 ~ /lm/){ cpubit=64 }else if ($2 ~/pm/){ cpubit=32 }else if($2 ~ /rm/){ cpubit=16 }else if (systembit ~ /i386|i586|i686/){ cpubit=32 }else{ cpubit=systembit } } }END{ cpu_phy=length(cpuphy) if (cpu_phy == 0 ){ cpu_phy=1 } print "|_. CPU Bit|",cpubit,"|" print "|_. CPU Type|",cputype,"|" print "|_. CPU Num|",cpu_phy,"|" print "|_. CPU Core|",cpucore,"|" }' #for awk above 4 version ,about dmidecode Information #/usr/sbin/dmidecode | awk -F\: 'BEGIN{ # in_section=0 # end=1 # num=1 # area["System Information"]="Product Name:,Manufacturer:" # area["Memory Device"]="Type:,Size:" # }{ # if (!end){ # if (in_section){ # if ( $0 ~ /^$/ ){ # end=1 # }else{ # for (k in info){ # if ( $0 ~ info[k] ){ # key=areainfo" "info[k] # if ( key in result) { # if ($2 in result[key]){ # result[key][$2]++ # }else{ # result[key][$2]=1 # } # }else{ # result[key][$2]=1 # } # } # } # } # } # }else if ($0 in area ){ # end=0 # in_section=1 # areainfo=$0 # split(area[$0],info,",") # } #}END{ # for (row1 in result){ # row3="" # for (row2 in result[row1]){ # if (result[row1][row2] > 1){ # row3=row3" "row2"*"result[row1][row2] # }else{ # row3=row3" "row2 # } # } # print "|_. "row1,"|",row3,"|" # } #} #' #for awk 3 version ,about dmidecode Information /usr/sbin/dmidecode | awk -F\: 'BEGIN{ in_section=0 end=1 num=1 area["System Information"]="Product Name:,Manufacturer:" area["Memory Device"]="Type:,Size:" }{ if (!end){ if (in_section){ if ( $0 ~ /^$/ ){ end=1 }else{ for (k in info){ if ( $0 ~ info[k] ){ key=areainfo" "info[k]"|"$2 if ( key in result) { result[key]++ }else{ result[key]=1 } } } } } }else if ($0 in area ){ end=0 in_section=1 areainfo=$0 split(area[$0],info,",") } }END{ for (key in result){ split(key,row,"|") if (row[1] in arr){ if (result[key] > 1){ arr[row[1]]=arr[row[1]]" "row[2]"*"result[key] }else{ arr[row[1]]=arr[row[1]]" "row[2] } }else{ if (result[key] > 1){ arr[row[1]]=row[2]"*"result[key] }else{ arr[row[1]]=row[2] } } } for (k in arr){ print "|_.",k,"|",arr[k],"|" } } ' #Disk Information df -hPT -x tmpfs -x devtmpfs -x rootfs | awk '{ if($0 ~ /dev/){ disk=disk""$1"=>"$7"=>"$3" " } }END{ print "|_. Disk |",disk,"|" }' echo #Show Hard_Info echo -e "h2. Filesystems \n" df -hPT -x tmpfs -x devtmpfs -x rootfs | awk '{print "|" $1 " |" $2 " |" $3 " | " $6 " | " $7 " |"}' echo #IDE devices information echo -e "h2. IDE devices \n" if [ "$(ls -1d /proc/ide/hd* 2> /dev/null)" ]; then for DEV in `ls -1d /proc/ide/hd* |sed 's/.*\///'` do MODEL=`cat /proc/ide/$DEV/model` if [ -e /proc/ide/$DEV/capacity ]; then SIZE=`cat /proc/ide/$DEV/capacity` SIZE=`expr $SIZE / 2097152` else if [ -e /sys/block/$DEV/size ]; then SIZE=`cat /sys/block/$DEV/size` SIZE=`expr $SIZE / 2097152` else SIZE='(unknown)' fi fi echo "| /dev/$DEV | $MODEL | $SIZE GB |" done fi echo #SCSI devices infomation if [ "$(ls -1d /sys/block/sd* 2> /dev/null)" ]; then echo -e "h2. SCSI devices \n" echo "| devices |_. Model |_. Size |" for DEV in `ls -1d /sys/block/sd* |sed 's/.*\///'` do MODEL=`cat /sys/block/$DEV/device/model` SIZE=`cat /sys/block/$DEV/size` SIZE=`expr $SIZE / 2097152` echo "| /dev/$DEV | $MODEL | $SIZE GB |" done fi echo #open port and services #notice need root permission echo -e "h2. Open Port and Serivce \n" /bin/netstat -tln | awk '{ if($0 ~ /^tcp/){ split($4,port,":") num=length(port) cmd="lsof -i :"port[num] while(( cmd | getline foo ) > 0 ){ if(!(foo ~ /^COMMAND/ )){ split(foo,ccc," ") abc[port[num]]=ccc[1]" "abc[port[num]] } } close(cmd) } }END{ print "| Port | Service|" for (key in abc){ value="" split(abc[key],com," ") for ( a in com){ uniqcmd[com[a]]++ } for (b in uniqcmd){ value=b"*"uniqcmd[b]" "value } delete uniqcmd print "|",key,"|",value,"|" } print "" }' function grep_towiki(){ if [ -f $1 ];then echo -e "h3. $1 \n" echo "<pre>" /bin/egrep -v "^#|^$" $1 echo "</pre>" echo fi } echo -e "h2. Auto Start \n" grep_towiki /etc/rc.local echo -e "h3. chkconfig \n" echo "<pre>" /sbin/chkconfig --list |awk '{if ($0 ~ /3:on/){print $1}}' echo "</pre>" echo echo -e "h2. Crond Tab \n" for file in /var/spool/cron/* ;do grep_towiki $file done echo -e "h2. System Kernel File \n" grep_towiki /etc/sysctl.conf echo -e "h2. Rsync Config \n" grep_towiki /etc/rsyncd.conf

更新redmine wiki 

批量更新redmine的服务器信息 结合pssh

#!/bin/bash 
#shell import wiki
#need bash
author_id=3
wiki_id=6
protected=0
default_page_id=215
log_dir=/tmp/serversout
hostlist=$1

export PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/bin:/usr/local/webserver/mysql/bin

pssh -t 600 -h $hostlist -l root -o $log_dir "bash /home/sh/ServerInfo_wiki_v2.sh"

SQL()
{
mysql -h192.168.1.253 -uUser -pPassword redmine <<EOF
$1
EOF
}


for ip in `cat $hostlist | grep -v "#"`
do
    title=`echo $ip | sed 's/\.//g'`
    text=`cat $log_dir/$ip | awk '{printf($0"\\n");}'`
    updated_on=`date +%Y"-"%m"-"%d" "%k":"%M:"%S"`

    wiki_page_select="select id from wiki_pages where title='$title';"

    echo $wiki_page_select

    page_id=`echo $(SQL "$wiki_page_select") | awk '{print $2}'`

    #echo $page_id

    if [ ! -z $page_id ] ;then

        wiki_content_id=$page_id

        wiki_contents_update_SQL="update wiki_contents set text='$text',updated_on='$updated_on' where page_id=$page_id "

        wiki_version_select="select version from wiki_content_versions where page_id=$page_id"

        version=`echo $(SQL "$wiki_version_select") | awk '{print $NF+1}'`

        wiki_versions_insert_SQL="insert into wiki_content_versions values('','$wiki_content_id','$page_id','$author_id','$text','','','$updated_on','$version')"

        #deBug
        #echo $wiki_contents_update_SQL
        #echo $wiki_versions_insert_SQL

        SQL "$wiki_contents_update_SQL"
        SQL "$wiki_versions_insert_SQL"

        echo "[INFO] $ip is updated "

    else

        version=1
        wiki_pageid_select="select page_id from wiki_contents where id=(select max(id) from wiki_contents); "
        default_page_id=`echo $(SQL "$wiki_pageid_select") | awk '{print $2}'`

        ((default_page_id=${default_page_id}+1))

        wiki_content_id=$default_page_id
        echo content_id=$wiki_content_id

        #insert wiki_page
        wiki_page_SQL="insert into wiki_pages values('','$wiki_id','$title','$updated_on','$protected',NULL)"

        #insert wiki_contents
        wiki_contents_SQL="insert into wiki_contents values('','$default_page_id','$author_id','$text','','$updated_on','$version')"

        #insert wiki_content_versions
        wiki_content_versions_SQL="insert into wiki_content_versions values('','$wiki_content_id','$default_page_id','$author_id','$text','','','$updated_on','$version')"


        #deBug
        #echo $wiki_page_SQL
        #echo $wiki_contents_SQL
        #echo $wiki_content_versions_SQL

        SQL "$wiki_page_SQL"
        SQL "$wiki_contents_SQL"
        SQL "$wiki_content_versions_SQL"

        echo "[INFO] $ip is inserted "
    fi
done

执行 bash servers_to_wiki_mysql.sh /tmp/cn.list

 该脚本可以新建也可以更新redmine wiki中的服务器信息, 但一定要注意参数变量的替换 不然会破坏原有的redmine哦

output wiki

 

输出excel格式

如果是服务器信息对比这样看wiki也是挺累的,可能你比较习惯用excel的格式进行排序尤其是大量服务器的时候。

我们再将输出的数据转换成excel来进行对比查看。

#!/bin/bash 
#shell import excel
#need bash
log_dir=/tmp/serversout
hostlist=$1
outexcel=$2

#export PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/bin:/usr/local/webserver/mysql/bin

#pssh -t 600 -h $hostlist -l root -o $log_dir "bash /home/server/code/ServerInfo/ServerInfo_wiki_v2.sh"

#print title 
for ip in  `grep -v "#" $hostlist | head -1` 
do
    cat $log_dir/$ip | awk -F\| \
        -v out=$outexcel \
        '{
     if($0 ~ /^\|_./){
         split($2,ti,"_.")
        printf "%s|",ti[2] > out
    }
    
}'
done
echo  >> $outexcel
for ip in `grep -v "#" $hostlist`
do
    cat $log_dir/$ip | awk -F\|  \
    -v out=$outexcel \
    '{
    if($0 ~ /^\|_./){
        info[row1]=info[row1]$3"|"
    }
}END{
    for (key in info){
        print key""info[key] >> out
    }
}'
    done

执行 bash servers_to_excel.sh /tmp/cn.list /tmp/cn.xls

得到如下列表 这样就很方便的进行比较和取舍了。PS:打开cn.xls 使用 | 为分割符就可以了。

output excel

 

Shell: 管理相片和视频的脚本

作者:Ajian 发布时间:June 20, 2012 分类:Shell

  有时需要修改大量的相片或者视频,就会写点小脚本进行批量的处理,没有太整理,像有批量下载,批量修改后缀,视频格式转换。今天处理的时候用了这两个脚本,不注意的时候放在目录里面都快找不到了。

脚本一:

相片的后缀修改,像我的松下LX5保存的相片为大写的JPG,可是很多网站上传都不知道大写的,我就郁闷,多匹配一个大写的会需要多少代码。只好自己修改了。将后缀的大写转换为小写

#!/bin/sh
#basename changesuffix.sh
##change the file suffix [A-Z] to [a-z]
dir=$1

for a in `find $dir -type f -name "*.*"`
do
 suffixname=`echo $a | awk -F. '{print $NF}'`
echo $suffixname
 fsuffixname=`echo $suffixname | tr "[A-Z]" "[a-z]"`
echo $fsuffixname
 holdname=`echo $a | awk -F. '{print $1}'`
echo $holdname
if [ $suffixname != $fsuffixname ];then
echo "change the $a"
mv $a $holdname.$fsuffixname
fi
done

脚本二:

我在You(你明白的)tube上下载了大量的MP4,想放到MP3里面听听,而且传输也要小很多,默认的MP4很高清体积很大。弄个批量转换的相当不错。

#!/bin/bash
srcDir=/root/Videos/Music
desDir=/root/MP3

cd $srcDir
for file in *.mp4 
do 
    filename=`echo "$file" | awk -F'.mp4' '{print $1}'`
    if [ ! -e  $desDir/"$filename".mp3 ];then
        ffmpeg -i "$file" -f mp3 -ab 192000 -vn $desDir/"$filename".mp3
    fi
done

脚本都是应需求很快写的,很简单没有格式没有扩展,还有些现在找不到了,哈哈,哪天看到了再说。

Golang:So easy 命令行参数传递Flag 对比Shell python

作者:Ajian 发布时间:April 13, 2012 分类:Shell,Golang

    感叹Go language写参数传递太简单了,so easy .如果要写脚本或者写服务肯定很多时候都要进行命令行参数的传递。

如:

./flag  --config=main.xml --port=7788

对比shell和python就感觉go真是很简单方便。下面对比下几个语言吧:

Shell

Shell使用了两个getopts和getopt getopts可以接受短命令 getopt就可以复杂一些 包括长命令 而且两个命令是分开的 感觉很原始

getopts 短命令

#!/bin/bash
if [ $# -eq 0 ];then
    echo "erminating..." >&2 ; exit 1 ;
else
    while getopts ":c:p:" opt; do
        case $opt in
            c)
                config=$OPTARG 
                echo "Option c, argument $config" 
                ;;
            p)
                port=$OPTARG 
                echo "Option p, argument $port"
                ;;
            *)
                echo "nternal error!" ; exit 1 ;;
        esac
    done
fi

Output:

# bash shortflag.sh -c config.xml -p 8080
Option c, argument config.xml
Option p, argument 8080

getopt长命令

#!/bin/bash
TEMP=`getopt -o p:c: --long port:,config: \
      -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true ; do
        case "$1" in
                -p|--port) echo "Option p, argument \`$2'" ; shift 2 ;;
                -c|--config)echo "Option c, argument \`$2'" ; shift 2 ;;
                --) shift ; break ;;
                *) echo "Internal error!" ; exit 1 ;;
        esac
done
echo "Remaining arguments:"
for arg do
   echo '--> '"\`$arg'" ;
done

Output:

# bash longflag.sh -c config.xml -p 8080
Option c, argument `config.xml'
Option p, argument `8080'
Remaining arguments:
# bash longflag.sh --config=config.xml --port=8080
Option c, argument `config.xml'
Option p, argument `8080'
Remaining arguments:

Python

同样有一个getopt函数不过 该函数已经包括了长命令格式 做为可选的一个参数 进化了。

#!/usr/bin/python
import getopt, sys
def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "p:c:", ["port=", "config="])
    except getopt.GetoptError:
        print "Terminating..." 
        sys.exit(2)
    config = None
    port = 8001
    for o, a in opts:
        if o in ("-p", "--port"):
            port= a
            print "Option p, argument %s"%port
        if o in ("-c", "--config"):
            config = a
            print "Option c, argument %s"%config

if __name__ == "__main__":
    main()

Output:

# python flag.py  --port=9000 --config=file.xml
Option p, argument 9000
Option c, argument file.xml
# python flag.py  -p 9000 -c file.xml
Option p, argument 9000
Option c, argument file.xml

看到上面这些如果不认真看 还真容易晕吧。到Go了 想像不到的简单。

Go

使用flag库,一切都简化了

package main

import (
    "flag"
    "fmt"
)

var (
     openPort= flag.String("port",":8080","http listen port")
     configFile= flag.String( "config","","config file name" )
)

func main(){
    flag.Parse()
    fmt.Println("Configure Filename: ",*configFile)
    fmt.Println("Open Port: ",*openPort)
}

Output:

#./run flag.go
# ./flag -port 8001 -config mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag --port 8001 --config mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag --port=8001 --config=mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001
# ./flag -port=8001 -config=mirror.xml
Configure Filename:  mirror.xml
Open Port:  8001

看到结果了吧 只要一定义 引用可以任意的 不用纠结他是怎么工作的 要的就是快捷。

里面有个run是我的小脚本,可以方便的帮助我编译做测试的脚本

#!/bin/bash
filename=$1
outfile=`echo ${filename%.*}`
go build -o $outfile $filename

更多flag函数的使用去官网查看吧: http://golang.org/pkg/flag

[Shell]打乱排序

作者:Ajian 发布时间:May 4, 2011 分类:Shell

一般我们都会使用排序功能,但也会遇到要打乱排序的情况,之前我有一个脚本是统计所有平台的名字,默认就会按名字进行有序的排列,但一台server上可能在同时并发上10个连接的时候会出现被拒绝的情况,所以要打乱整个排序,让同时10个并发不是请求到1台server上面。所以打乱排序就变得有意义了。

* 一行中各列打乱

awk 'BEGIN{srand()}{for(i=1;i<=NF;i++) b[rand()NF]=$i}END{for(x in b)printf "%s ",b[x]}'` data

* 一个文本中各行打乱

awk 'BEGIN{srand()}{b[rand()NR]=$0}END{for(x in b)print b[x]}' data

[Shell] 巧用rename批量修改文件名

作者:Ajian 发布时间:February 24, 2011 分类:Shell

批量修改文件名,是很多人遇到的问题,很多人都利用for find mv sed awk来进行批量的修改,但是利用好了rename其实批量修改就是一条简单命令解决的问题。
实例:
原文件名:
./20110214/xxmm_api_20110214.ip-11-98-241-106.report
./20110214/xxmm_total_20110214.ip-11-98-241-106.report
./20110214/xxmm_user_20110214.ip-11-98-241-106.report
./20110214/xxmm_20110214.ip-11-98-241-106.log
要修改成:
./20110214/yymm_api_20110214.ip-11-98-241-106.report
./20110214/yymm_total_20110214.ip-11-98-241-106.report
./20110214/yymm_user_20110214.ip-11-98-241-106.report
./20110214/yymm_20110214.ip-11-98-241-106.log
而且是有很多这样的目录 另外还有一些 xxmm_uid*.report的文件又是不能被修改的。
现在不需要使用那些复杂的循环进行处理直接一条命令搞定。
rename xxmm yymm ./*/xxmm_{api,total,user}_*.{report,log}
xxmm 到 yymm 或者其它名称为了更方便的通用,
写一个简单的脚本prename.sh

#!/bin/bash
oldprefix=$1
newprefix=$2
weblogpath=$3
weblogpath=${weblogpath:-"/data2/pic/weblog"}
renamepetparty(){
mkdir -p test && touch ./test/${oldprefix}_{api,total,user}.{report,summary}
rename $oldprefix $newprefix ./*/${oldprefix}_{api,total,user}*.{report,summary}
}
if [ -d "$weblogpath" ];then
cd $weblogpath && renamepetparty
rm -rf test
fi

使用方法:
bash prename.sh xxmm yymm
或者如果不是默认的/data2/pic/weblog 指定目录
bash prename.sh xxmm yymm /data2/log
NOTICE:
mkdir -p test && touch ./test/${oldprefix}_{api,total,user}.{report,summary}
为了解决rename的一个BUG ,在批处理的时候一旦没有匹配到其中一个都会进行中断。
如:./*/${oldprefix}_api*.report 都匹配不到 那么就不会再继续下去,批处理也就失去意义了。
这个BUG真是让人郁闷,所以办法就是要么写成多条进行多种情况的替换,要么就是先创建后替换。

=====================华丽分割线==========================
悲剧的rename bug
rename虽然是批量修改 尤其手动修改的时候相当的方便,但是他的批量是有上限的。
由于我替换的都是365*7*N的量 他就直接报 参数过多
rename :Argument list too long
只好再一次修改脚本

#!/bin/bash
oldprefix=$1
newprefix=$2
weblogpath=$3
weblogpath=${weblogpath:-"/data2/pic/weblog"}

renamepetparty(){
         touch ${oldprefix}_{api,total,user}_temp8888.{report,summary}
         rename $oldprefix $newprefix ${oldprefix}_{api,total,user}*.{report,summary}
}

if [ -d "$weblogpath" ];then
    for i in `ls $weblogpath`
    do
        cd $weblogpath/$i ; renamepetparty
    done
fi

find $weblogpath -name "*_temp8888*" -type f | xargs rm -f 

增加一个for循环进入每一个目录进行替换,但还是同样要面对上一个BUG的问题 所以在每个目录也创建一个临时文件
添加上“ temp8888”这样的标记就不会跟原来的文件有冲突了,最后还是要批处理删除的。

可爱又可气的rename命令。