初试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有了新的认识,及时记录遇到的问题,相信下次会更有印象。使用脚本,可以让工作更有效率,相信以后也会越用越多。
初试Shell脚本的更多相关文章
- 第一个shell脚本
打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好. #!/bin/bash echo "Hello World !" &quo ...
- 使用C#给Linux写Shell脚本
在这个逼格决定人格,鄙视链盛行的年头,尤其是咱们IT界,请问您今天鄙视与被鄙视的次数分别是多少?如果手中没有一点压箱的本事,那就只有看的份了.今天我们也要提升下自己的格调,学习些脑洞大开的东西,学完之 ...
- shell脚本规划化模板
shell脚本规划化模板 Linux运维过程中,shell脚本是不可缺少的工具,但是每个运维人员编程的习惯都不一样,很多时候就是实现某个功能,写出来的脚本都是烂七八糟的.脚本必须规范化,应该从以后几个 ...
- Shell脚本编程30分钟入门
Shell脚本编程30分钟入门 转载地址: Shell脚本编程30分钟入门 什么是Shell脚本 示例 看个例子吧: #!/bin/sh cd ~ mkdir shell_tut cd shell_t ...
- Linux Shell脚本逻辑操作符简介
在写程序时,会用到条件判断,测试条件是否成立.很多时候,判断条件是多个的,这个时候需要用到逻辑操作符.shell脚本中常用的有哪些逻辑操作符呢? 1.逻辑与: -a 格式: conditon1 -a ...
- Linux shell脚本编程(三)
Linux shell脚本编程 流程控制: 循环语句:for,while,until while循环: while CONDITION; do 循环体 done 进入条件:当CONDITION为“真” ...
- Linux shell脚本编程(二)
Linux shell脚本编程(二) 练习:求100以内所有偶数之和; 使用至少三种方法实现; 示例1: #!/bin/bash # declare -i sum=0 #声明一个变量求和,初始值为0 ...
- Linux shell脚本编程(一)
Linux shell脚本编程: 守护进程,服务进程:启动?开机时自动启动: 交互式进程:shell应用程序 广义:GUI,CLI GUI: CLI: 词法分析:命令,选项,参数 内建命令: 外部命令 ...
- 详解Linux交互式shell脚本中创建对话框实例教程_linux服务器
本教程我们通过实现来讲讲Linux交互式shell脚本中创建各种各样对话框,对话框在Linux中可以友好的提示操作者,感兴趣的朋友可以参考学习一下. 当你在终端环境下安装新的软件时,你可以经常看到信息 ...
随机推荐
- oc消息转发:forwardInvocation、签名、参量个数、SEL 相关测试
结论1.签名的参量类型伪造不正确会导致崩溃. 结论二.签名个数不对可能会导致参量丢失. 结论三:在签名配置正确的情况下,系统会将函数调用的所有信息打包到NSInvocation准备转发: - (voi ...
- gcd?人生赢家!
题目背景 原创:b2019dy gcd是一个热爱游戏的人 题目描述 gcd最近在玩一个有趣的游戏 我们把这个游戏抽象成一张图,图上有n个点,我们需要寻找总计m件宝物,它们分布在图上,对于每件宝物而言, ...
- centos中安装、升级git
yum install git 若是从老版本升级,则按下面方法.(centos中) 先更新系统sudo yum update 安装依赖的包yum install curl-devel expa ...
- datagrid 完整dom结构
<!-- datagrid的最外层容器,可以使用$(target).datagrid('getPanel')或者$.data(target,'datagrid').panel得到这个DOM对象, ...
- Spring源码分析(二十)准备环境
摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. prepareRefresh函数主要是做些准备工作,例如对系统属性及环 ...
- 微信公众号开发 [03] 结合UEditor实现图文消息群发功能
0.写在前面的话 如何实现微信平台后台管理中的,图文消息发送功能? 大概的过程如下: 通过类似表单的形式,将文章各部分内容提交到后台,封装成一个实体类,并持久化到数据库中 需要推送的时候,将不同的文章 ...
- Windows下修改iTunes备份路径
0.准备工作: 关闭itunes 在任务管理器中杀掉iTunes开头的服务 1,找到iTunes默认备份路径:C:\Users\xxx\AppData\Roaming\Apple Computer\M ...
- C++编写DLL动态链接库的步骤与实现方法
原文:http://www.jb51.net/article/90111.htm 本文实例讲述了C++编写DLL动态链接库的步骤与实现方法.分享给大家供大家参考,具体如下: 在写C++程序时,时常需要 ...
- Oracletop10物理段
Oracletop10物理段 select owner, name, type, mega, tbs from (select owner, case when segment_type = 'LOB ...
- 2017-2018-1 20155210 《信息安全系统设计基础》 实现mypwd
2017-2018-1 20155210 <信息安全系统设计基础> 实现mypwd 作业要求: 1.学习pwd命令 2.研究pwd实现需要的系统调用(man -k; grep),写出伪代码 ...