• 初试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有了新的认识,及时记录遇到的问题,相信下次会更有印象。使用脚本,可以让工作更有效率,相信以后也会越用越多。

  • 相关阅读:
    Java异常处理机制(转)
    深入探讨 java.lang.ref 包(转)
    一篇不错的讲解Java异常的文章
    Java国际化学习(一)介绍
    Java泛型集合排序(转)
    Java常见异常总结 (转)
    java中的异常处理机制
    深入分析 Java I/O 的工作机制(转)
    java里BufferedReader和Scanner
    JS深入学习知识整理
  • 原文地址:https://www.cnblogs.com/vanch/p/9682554.html
Copyright © 2020-2023  润新知