博客已迁移,新地址

----------------------------------------------------------------------------------------

在实际工作中,需要对shell脚本进行正确性测试。

如何用最快最有效的方式进行测试?

很多开发的习惯是,二话不说,写完/拿到,就跑一把,看看输入,输出,想要的操作是否完成,也就过了。

其实这是十分不严谨的,若是未经过QA,风险还是相当大的。

以下即shell脚本测试流程,仅供参考

1.代码走读:

写完,或者拿到一个shell脚本,不必急于运行,虽然实践是检验整理的唯一标准,但是,在读代码这个过程中,可以规避很多低级的bug.

读什么?

A.代码逻辑,这个脚本用来做什么,主要分为多少步,分别做了什么事情?

用于检查是否有遗漏逻辑,或有悖于需求。

B.具体语法,变量,判断语句

语法方面的东西,变量是否定义,判断语句逻辑是否正确,是否考虑各种异常,错误是否退出,返回正确状态值等。

2.语法检测:

shell的语法还是相当让人无语的,很多很容易疏忽遗漏的地方

命令格式: sh -n ***.sh

若是没有异常输出,证明脚本没有明显的语法问题。

3.运行跟踪:

实践是检验整理的唯一标准,跑一把。

不过,可不是直接运行然后去看最终结果,这样会遗漏掉很多中间过程。

命令格式: sh -vx ***.sh

得到效果如下:

我们可以看到

每行代码原始命令(无+的):[这是-v的效果]

代码执行时的情况(带+),包括运算结果,逻辑判断结果,变量赋值等等[-x的效果]

而我们所要关注的就是这些信息,主要是变量值和逻辑判断结果。

4.覆盖分支:

直接跑,只能覆盖到主体流程,对于其他控制流分支的代码是无法覆盖到的。

对于关键性的,重点的逻辑,我们需要制造条件,使运行脚本可以进入对应分支

5.其他:

A.关于bashdb:

可以尝试下,但是感觉投入产出比不高

B.关于单元测试:

实际工作中,由于项目压力比较大,单元测试的成本还是相当高的,所以目前为止没有。

6.有没有更好的方式?

好吧,单步跟踪,脚本短的还好,日志信息不会太多,要是多了,存在调用其他脚本等等.....

日志量达到几千行,这是很轻易的事情。

跟踪过的童鞋有同感,展现不够友好,惨白惨白一片,一千行下来,看的眼花。

很容易遗漏(LZ被坑了好多回,你看,或不看......错误信息明明就在那里,就是视而不见)

So.进行了一层优化,对日志进行处理,使用正则,标注我关心的信息

效果图对比:

处理后:(对错误,关键信息进行颜色标记,在linux终端可以显示)

脚本是用python实现的,位置:https://github.com/wklken/pytools/tree/master/shell

思想是:执行,抓到所有日志,用正则进行匹配,打上颜色,然后输出

欢迎一起优化,使之功能更完善

代码:

#!/bin/env python
#-*- coding:utf-8 -*-
#@Author: wklken
#@Mail: wklken@yeah.net ,lingyue.wkl@taobao.com
#@Date: 20120706
#@Version: 0.1  sh -n, check for static syntax
#          0.2  sh -vx, color the output log which i care about
#          0.2.1 rebuild all functions , lines  200+ -> 120
#          0.2.2 refine the re pattern.
#          0.2.3 add sh params support. fix bug and add re patterns
#          0.2.5 add warn/error pattern and collect the result
#          0.2.6 use decorator to refine print, refine collect method#@Desc: Quick test shell script.The target is hacking into it and get all the status i need.
#TODO: need to keep source code in 200 lines! refine!import sys,os
import commands
import re#color defined
COLOR_NONE = "C_NONE"
COLOR_GREEN = "C_G"
COLOR_RED = "C_R"
COLOR_YELLOW = "C_Y"
COLOR_PURPLE = "C_P"
COLOR_BLUE = "C_B"COLOR_MAP = {COLOR_NONE : "\033[m",COLOR_GREEN : "\033[01;32m",COLOR_RED : "\033[01;31m",COLOR_YELLOW : "\033[01;33m",COLOR_PURPLE : "\033[01;35m",COLOR_BLUE : "\033[01;34m",None:"\033[m" }#the command used defined
SH_N = "sh -n "
SH_X = "sh -vx "
LOG_BEGIN = "export PS4='+${BASH_SOURCE}|${LINENO}|${FUNCNAME[0]} -> ';"
LOG_BEGIN = ""#the type of output log line
LINE_TYPE_CMD = "CMD"
LINE_TYPE_EXC = "EXC"
LINE_TYPE_CMT = "CMT"CMD_Y = COLOR_MAP.get(COLOR_YELLOW) + "CMD: " + COLOR_MAP.get(COLOR_NONE)#----------pattern used to match begin -----------------------------
#0. special
PATTERN_ADDSIGN = re.compile("(^\++)")#1. execute command log match pattern
exc_mark_pattern = [(r"([\[\]])", COLOR_YELLOW), #for condition testing   must be the first one(r"(([12]\d{3})(1[12]|0[1-9])(0[1-9]|1\d|2\d|3[01]))",COLOR_PURPLE), #date yyyyMMDD(r"(tbsc-dev)", COLOR_RED),  # path: tbsc-dev(r"([a-zA-Z_][a-zA-Z0-9_]*=[\s|\"\"]*)$",COLOR_RED),   # params=None(r"(exit\s+-?\d*|return\s+-?\d*)",COLOR_BLUE), #exit status(r"(\s(\-[acbdefgnorsuwxzL]|\-(lt|le|gt|ge|eq|ne))\s)", COLOR_YELLOW),(r"((\s(=|==|<=|>=|\+=|<|>|'!='|\&\&)\s)|'!')", COLOR_YELLOW),(r"(\s(\-input|\-output|\-i|\-o)\s)", COLOR_YELLOW),]
EXC_MARK_PATTERN = [(re.compile(s),color) for s,color in exc_mark_pattern]#2. error/warn result log match pattern
# 100% error
error_mark_pattern = [(r"(No such file or directory|command not found|unknown option|invalid option)",COLOR_RED), #result -> file not found(r"(unary operator expected)",COLOR_RED), # test failed(r"(Permission denied)",COLOR_RED),(r"(syntax error|unexpected|read error)",COLOR_RED),(r"(java.io.FileNotFoundException|org.apache.hadoop.mapred.InvalidInputException|java.lang.IllegalMonitorStateException)", COLOR_RED),#javaerror]
ERROR_MARK_PATTERN = [(re.compile(s),color) for s,color in error_mark_pattern]# may be not error ,just warn,notice
warn_mark_pattern = []
WARN_MARK_PATTERN = [(re.compile(s),color) for s,color in warn_mark_pattern]#3. command log match pattern
cmd_mark_pattern = error_mark_pattern + warn_mark_pattern + \[(r"(line \d+)", COLOR_RED), #error report the line No(r"(\$(\{\w+\}))", COLOR_PURPLE),(r"(\.\.)",COLOR_PURPLE), #相对路径(r"((?<!-)\b(\w+)\b=)", COLOR_YELLOW),(r"(\$(\w+))", COLOR_PURPLE), #变量名(r"(\w+\.sh\s*)", COLOR_GREEN), #*.sh(r"(`)", COLOR_GREEN),  # ``(r"(\s?\w+\s*\(\))", COLOR_GREEN), #function()(r"(\{\s*$|^\}\s*$)", COLOR_GREEN), # function {}(r"(^export\s|^source\s)", COLOR_YELLOW),(r"(\|)", COLOR_GREEN),(r"(<<|>>|<|>)", COLOR_YELLOW),]
CMD_MARK_PATTERN = [(re.compile(s),color) for s,color in cmd_mark_pattern]
#----------pattern used to match end -----------------------------#static params defined
error_lines = []#functions begin
def str_coloring(str_info, color=COLOR_NONE):"""color str"""return COLOR_MAP.get(color, COLOR_MAP.get(None)) + str_info + COLOR_MAP.get(COLOR_NONE)def print_symbol(str_info):"""print the symbol"""print "-"*20 + str_info + "-"*20def wrap_print_func(arg):"""wrap func, print begin and end sign"""def  newfunc(func):def newfunc_withparams(*args, **kwargs):print_symbol(arg+" BEGIN")func(*args, **kwargs)print_symbol(arg+" END")return newfunc_withparamsreturn newfunc@wrap_print_func("STATIC SYNTAX")
def static_syntax_check(file_path):"""Check the static syntax"""cmd = SH_N + file_pathresult = commands.getoutput(cmd)if result:print "script syntax check:"+str_coloring(" FAILED", COLOR_RED)print str_coloring(result,COLOR_RED)else:print "script syntax check:"+str_coloring(" PASS", COLOR_GREEN)def pre_handler(result):"""pre handle the result lines """pass@wrap_print_func("PROCESS LOG CHECK")
def dynamic_log_process(file_path, params):"""Process the log of sh script"""cmd = LOG_BEGIN + SH_X + file_path + " " + paramsresult = commands.getoutput(cmd)pre_handler(result)process_line(result)def cmd_type(line):"""return the type of line,and can do something with it"""if line.startswith("+"):return LINE_TYPE_EXC,lineelif line.lstrip().startswith("#"):return LINE_TYPE_CMT,lineelse:#return LINE_TYPE_CMD, CMD_Y + linereturn LINE_TYPE_CMD,linedef mark_sign_by_pattern(line, line_type=LINE_TYPE_EXC):"""mark the str by pattern"""#can't use in py2.4,ni mei a#use_pattern = EXC_MARK_PATTERN if line_type == LINE_TYPE_EXC else CMD_MARK_PATTERNif line_type == LINE_TYPE_EXC:use_pattern = EXC_MARK_PATTERNelse:use_pattern = CMD_MARK_PATTERNnative_line = linefor pt,color in use_pattern:m = pt.findall(line)if m:line = pt.sub( COLOR_MAP.get(color)+r"\1"+COLOR_MAP.get(COLOR_NONE), line)for pt,color in ERROR_MARK_PATTERN:e = pt.findall(native_line)if e:error_lines.append(line)return linedef process_line(result):"""format each line.With the pattern"""lines = result.split("\n")for line in lines:line_type, line = cmd_type(line)if line_type == LINE_TYPE_EXC:result = mark_sign_by_pattern(line, line_type)print PATTERN_ADDSIGN.sub(COLOR_MAP.get(COLOR_GREEN)+r"\1"+COLOR_MAP.get(COLOR_NONE),result)elif line_type == LINE_TYPE_CMD:print mark_sign_by_pattern(line, line_type)elif line_type == LINE_TYPE_CMT:print line@wrap_print_func("RESULT COLLECT")
def warn_error_collect(collect_list, collect_type="ERROR"):print str_coloring("RESULT TYPE: " + collect_type, COLOR_GREEN)if len(collect_list):print str_coloring(collect_type+" FOUND: ", COLOR_RED) + str_coloring(str(len(collect_list)), COLOR_YELLOW) for line in collect_list:print lineelse:print str_coloring("NO " + collect_type + " FOUND", COLOR_GREEN)args = sys.argv[1:]
sh_name = args[0]
params = " ".join(args[1:])static_syntax_check(sh_name)dynamic_log_process(sh_name, params)warn_error_collect(error_lines, "ERROR")

好了,就这些

工具的实现是为了提高效率,节约时间。

The end!

wklken

Gighub: https://github.com/wklken

Blog: http://wklken.me/

2012-09-15

转载请注明出处,谢谢!

如何进行shell脚本正确性测试相关推荐

  1. shell脚本编程测试类型下

    一bash的数值测试 -v VAR 变量VAR是否设置 数值测试: -gt 是否大于greater -ge 是否大于等于 -eq 是否等于 -ne 是否不等于  not equal -lt 是否小于 ...

  2. shell脚本的测试与判断的基础实施

    一.条件测试:判断条件是否成立 1.条件测试的类型:文件测试:整数比较:字符串比较:逻辑测试 2.条件测试的语法:[ 操作符  条件表达式]等于 test  操作符  条件表达式 3.文件测试: 1) ...

  3. shell脚本条件测试、正整数字符串比较与if、case语句

    目录 条件测试 三种测试方法 选项 比较整数数值 字符串比较 脚本中常用命令 echo命令 date命令 cal命令 tr命令 cut命令 sort命令 uniq命令 cat多行重定向 if语句 分支 ...

  4. shell脚本--批量测试主机连通性ping IP

    2019-03-28 12:11:51 1.新建文本文件,保存要连接的IP地址 vi target_ip.txt 137.32.117.53 137.32.117.60 137.32.117.64 2 ...

  5. c语言脚本的软件测试,编写自动测试c语言程序的shell脚本

    [一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高? ] 根据这样的要求我编写了一个解决这个问题的程序: #include #in ...

  6. 一次shell脚本小事故,从中学习排错过程-软件测试

    一次shell脚本小事故,从中学习排错过程 事出,童鞋使用shell脚本搭建测试环境的过称中..... 配置环境变量文件:/etc/profile(用于升级JDK或其他) 手动编辑方法:vi /etc ...

  7. Linux常用命令练习题-shell脚本用法(3)

    题目 shell脚本中单引号.双引号.反引号.转义字符.(). { }的练习. 写一个shell脚本,测试一下在本网络中哪个ip地址是OK. 我的答案 shell脚本中单引号.双引号.反引号.转义字符 ...

  8. shell脚本中的特殊变量与if条件测试

    1.特殊变量 实际工作中我们不可避免的遇到一些xxxx.sh脚本文件,实际阅读shell脚本代码时经常会遇到很多特殊变量(例如:$0.$n.$#.$@.$*.$?.$$等),我们常常会被这些特殊符号折 ...

  9. MySQL【付诸实践 01】Linux 环境 MySQL 数据库备份 shell 脚本(脚本源码及说明+定时任务配置+数据库恢复测试)粘贴可以

    数据库备份的重要性不言而喻,备份的方法主要分为两大类,一是文件备份,二是数据库本身的备份机制binlog日志,今天先说说文件备份,就是将数据库[结构和数据]导出为文件. 1.备份脚本 在 /data/ ...

最新文章

  1. Spring MVC学习-------------訪问到静态的文件
  2. makefile中模式规则的引入和介绍------%:%.cpp
  3. SAP Spartacus My Company list focus事件触发后,控件border的默认效果
  4. php detect unicode,php-functions/unicode.php at master · xiilei/php-functions · GitHub
  5. freemarker面试_面试请不要再问我Spring ,阿里架构师吐血整理,这是对“Spring家族”最完美的诠释...
  6. art-template入门(五)之模板变量
  7. mybatis学习(22):查询排序
  8. Linux进程实践(2) --僵尸进程与文件共享
  9. 智慧楼宇管理运营端app、运维管理、工单管理、报修管理、维保管理、巡检查询、巡检管理、能源管理、维保查询、智慧社区、巡检统计、工单统计、能源管理、智能楼宇、设备监控、智能社区、系统运营、楼宇运维小程序
  10. python 协程小程序(草稿有待完善)
  11. [转]MySQL和SQLServer的比较
  12. python locust mqtt_Boomer 实战压测 mqtt,2w 并发轻松实现
  13. png转icon java,PNG转ICO - steambap的个人空间 - OSCHINA - 中文开源技术交流社区
  14. JavaC++题解与拓展——leetcode953.验证外星语【么的新知识】
  15. 四芯水晶头电话线的接法
  16. thinkphp之url的seo优化
  17. 电脑桌面点什么都是计算机,电脑桌面总是弹出广告怎么办?教你2种方法,轻松解决...
  18. word转pdf保持图片清晰度
  19. 计算机简单易懂知识,如何选购电脑主板?小白装机简单易懂的电脑主板选购知识指南...
  20. 单调函数有界性类题目解法

热门文章

  1. 修改Mysql密码(简单粗暴)
  2. ai直线怎么变折线_用Illustrator制作简单的折线图
  3. 计算机的逻辑电路是什么意思,CMOS逻辑电路,CMOS逻辑电路是什么意思
  4. 基于JSP的酒店登记预定系统的设计与实现
  5. 过滤掉URL中的参数部分
  6. 突发!字节跳动AI Lab总监李磊离职!加盟美国高校,曾为百度少帅科学家,交大ACM班成员...
  7. 华为“天才少年”的200万年薪里,藏着人力资源管理的下一个10年
  8. 隐私计算加密技术基础系列-Diffie–Hellman key exchange
  9. make[2]: *** [/home/nnnn/calibration/devel/lib/libcalibrationtoolkit.so] Error 1
  10. 进程和程序区别和联系