zoukankan      html  css  js  c++  java
  • 初试Shell脚本

    背景

    临上线前测试比较努力,遇到闪退或者其他问题,会把日志包打给我,由于app内存限制,目前每次打包都是1m大小,所以有时查找问题的上下文比较吃力。同时由于日志比较多,根据关键词过滤的需求越来越重要。

    于是决定学写脚本完成这个任务,根据我的要求,工作流程应该是传入压缩包,根据后缀名解压,根据日期排序后合并成一个文件,按需过滤关键词。


    先上代码

    #!/usr/bin/env bash
    # Created By Vanch at 2018/9/20
    
    printHelp() {
        echo "Uncompess log files from inputed zip"
        echo "Then Merge these logs to one file"
        echo "Supported file types: zip tar tar.gz tar.bz2"
        echo
        echo "Use -s for filtering socket result to socket.log"
        echo 
        echo "Have fun!"
    }
    
    #如果没输入参数,就打印帮助信息
    if [ $# -eq 0 ]; then
        printHelp
        exit 0
    fi
    
    #把长选项转到短选项
    for arg in "$@"; do
      shift
      case "$arg" in
        "--help")       set -- "$@" "-h" ;;
        "--version")    set -- "$@" "-v" ;;
        "--list")       set -- "$@" "-l" ;;
        *)              set -- "$@" "$arg"
      esac
    done
    #获取短选项
    OPTIND=1
    printS=false;
    while getopts "dmksahvl" opt; do
        case $opt in
            h) #输入为help,就打印帮助信息
                printHelp
                exit 0;;
            l) #支持单独获取支持文件后缀列表
                echo "Supported file types: zip tar tar.gz tar.bz2"
                exit 0;;
            v) #支持查找版本号
                echo "1.0.0"
                exit 0;;
            s) #过滤Socket
                printS=true;;
        esac
    done
    
    #获得压缩包地址
    file=${!#}
    #如果不存在就退出
    if [ ! -f "$file" ]; then
        echo "File not exist!"
        exit 0;
    fi 
    #获取压缩后缀
    fileName=`basename $file`
    suffix=${fileName#*.}
    #判断文件类型
    support=('tar','tar.gz','tar.bz2','zip')
    if [ -z `echo "${support[@]}" | grep -w "$suffix"` ] ; then
        echo "File type not support!"
        exit 0;    
    fi
    #拼接文件夹地址
    fileDir=$(dirname $file)/${fileName%%.*}
    if [ -d $fileDir ]; then
        rm -rf $fileDir
    fi
    mkdir $fileDir
    cd $fileDir
    #解压文件
    case $suffix in
        'tar')
            eval "tar xvf $file > /dev/null 2>&1";;
        'tar.gz')
            eval "tar zxvf $file > /dev/null 2>&1";;
        'tar.bz2')
            eval "tar jxvf $file > /dev/null 2>&1";;
        'zip')
            eval "unzip -o $file > /dev/null 2>&1";;
    esac
    echo 'Uncompass Success!'
    
    #获取日志列表,按排序合并到一个日志
    mergeFile=./merge.log
    logCount=0
    #搜索com开头的日志,按日期排序,用?临时代替空格
    for logName in `ls | grep 'com' | sort -n | tr " " "?"`; do
        logName=${logName//'?'/' '}
        cat ./"$logName" >> $mergeFile
        ((logCount++))
    done
    #不存在日志就打断
    if [ $logCount -eq 0 ]; then
        echo "Log not exist!"
        exit 
    fi
    echo 'Merge Success!'
    #打印socket
    if [ $printS = true ]; then
        cat $mergeFile | grep -i 'socket' >> ./socket.log
        echo 'Filter socket'
    fi
    

    遇到的问题

    查询了很多资料后写完了这个脚本,基本满足了我的需求,下面总结一下怎么解决遇到的问题。

    使用环境

    一开始学脚本时,书上都说#! /bin/bash,但是看项目中大神写的脚本,都是#!/usr/bin/env bash,有什么区别呢?

    脚本用env启动的原因,是因为脚本解释器在linux中可能被安装于不同的目录,env可以在系统的PATH目录中查找。
    同时,env还规定一些系统环境变量。

    不同的系统,解释器的路径可能也不同,所以使用绝对路径是比较危险的方式。通过从环境中查找,可以保证兼容性。


    获取选项

    开发中我们经常用到命令,这些命令一般都配合选项达到不同的效果,比如最常用的ls -al,通过-a来指定结果包含隐藏文件,通过-l达到列表显示的效果。

    通过查询相关资料,我发现获取选项普遍的做法是使用getopts命令,但是这个方法只能获取-h这种短选项,对于--help长选项就不行。

    第一种办法是换成getopt命令,但是并不是每个系统都支持这个命令。具体使用和getopts类似,比如getopt -o ab:c -l a-long:b-long

    第二种方法是把支持的长命令转成短命令,我使用的就是这种方式,相对来说比较容易理解,且case写的比较统一。通过shift取出参数,再set --的方式重写,最后OPTIND=1把指针指回第一个选项。


    文件路径和文件后缀

    按需求需要判断后缀名来解压,那么就需要判断tar.gz之类的问题。同时,如果传入的文件目录是隐藏目录,也会造成一定的障碍。我们假设传入文件路径为/a/.b/c.tar.gz

    ${param#pattern} 从param前面删除pattern的最小匹配

    ${param##pattern} 从param前面删除pattern的最大匹配

    ${param%pattern} 从param后面删除pattern的最小匹配

    ${param%%pattern} 从param后面删除pattern的最大匹配

    如果按照${fileName##*.}来截取,那么只能拿到gz
    如果按照${fileName#*.}来截取,拿到的又是b/c.tar.gz。那怎么办呢?

    好在有dirname可以直接获取文件路径,basename拿到文件名,单独对文件名进行${fileName#*.}就可以拿到tar.gz了。


    去除不必要的打印

    执行解压命令时,会打印解压步骤,一般来说也需要显示,那如果我们不想要打印出来呢?有一个办法就是在命令之后加上> /dev/null 2>&1

    /dev/null :代表空设备文件

    > :代表重定向到哪里,例如:echo "123" > /home/123.txt

    1 :表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于"1>/dev/null"

    2 :表示stderr标准错误

    & :表示等同于的意思,2>&1,表示2的输出重定向等同于1

    所以含义就是把命令输出结果和错误输出重定向,使得输出不在当前屏幕显示,由于null比较特殊,向这个文件输入等于进入黑洞,因此达到效果。


    数组与空格

    使用ls | grep的方式来过滤结果获取文件名数组的最大问题是,如果文件名包含空格,那么前后会被分割成两个单元,导致处理比较困难。

    比较讨巧的方法是临时用特殊符号代替空格,在使用时再替换回来。这种方法不会改变文件名,也不用写复杂的数组合并,比较符合简单的设计。

    tr " " "?"
    ${logName//'?'/' '}
    

    总结

    通过这次简单的脚本实验,对shell有了新的认识,及时记录遇到的问题,相信下次会更有印象。使用脚本,可以让工作更有效率,相信以后也会越用越多。

  • 相关阅读:
    常见寻找OEP脱壳的方法
    Windows内核原理系列01
    HDU 1025 Constructing Roads In JGShining's Kingdom
    HDU 1024 Max Sum Plus Plus
    HDU 1003 Max Sum
    HDU 1019 Least Common Multiple
    HDU 1018 Big Number
    HDU 1014 Uniform Generator
    HDU 1012 u Calculate e
    HDU 1005 Number Sequence
  • 原文地址:https://www.cnblogs.com/vanch/p/9682554.html
Copyright © 2011-2022 走看看