一段评价程序平均运行时间的shell脚本&shell脚本编写注意事项
2012-06-02 11:56
645 查看
#!/bin/bash usage() { echo "$0 -c command [-r repeat time]" } # average_time head_match_string filename average_time() { head=$1 filename=$2 minutes=`sed -n "s/^$head.* \([0-9.]\+\)m.*/\1/p" $filename` minute=0 for i in $minutes do minute=`echo "scale=5;$minute+$i" | bc` done seconds=`sed -n "s/^$head.*m\([0-9.]\+\)s/\1/p" $filename` second=0 for i in $seconds do second=`echo "scale=5;$second+$i" | bc` done second=`echo "scale=3;(($minute*60+$second)/($repeat_time*1.0))" | bc` second=`echo $second | sed 's/^\./0&/'` second=`echo $second | sed 's/$/s/'` echo $second } command="x" repeat_time="10" while [ $# -gt 0 ] do case $1 in -c) command=$2 shift ;; -h) usage exit 0 ;; -r) repeat_time=$2 shift ;; *) echo unexpected arg $1 exit 1 esac shift done if `test "$command" == 'x'`; then usage exit 1 fi rm -rf time.temp i=0 while [ $i -lt $repeat_time ] do (eval "time "$command) 2>>time.temp i=$((i+1)) done real_average_time=`average_time "real" "time.temp"` user_average_time=`average_time "user" "time.temp"` sys_average_time=`average_time "sys" "time.temp"` echo "repeate command '$command' for $repeat_time times" echo "average_real_time = $real_average_time" echo "average_user_time = $user_average_time" echo "average_sys_time = $sys_average_time"
很久没有写shell脚本了。现在流行的是python,perl,甚至ruby。脚本语言的语法更简单明了,字符串操作,正则表达式操作,数值计算,shell可以说是样样比不上。shell的用法晦涩难懂,又缺乏很全面且可以快速定位的文档。即便写这段小小的代码,也花了我两个小时的时间,一半时间在google,一半时间在testing。
这段程序的功能正如标题所说,是重复运行一个程序,用time记录其每次运行的时间,最后计算一个平均时间。不论实际如何,至少从理论上来说,平均值更为合理一些。
程序虽短,但我却遇到了shell编程的种种障碍和需要注意的地方。这里总结一下:
1. 首行用#!/bin/bash而非#!/bin/sh。因为shell有许多中,如ubuntu中sh其实是dash(一个比bash更轻便快捷的shell,相应的,它的功能要比bash少很多)。把执行shell统一成流行的bash,而非任系统摆布,大概是我唯一能做的脚本可移植性方面的努力(^_^)!。
2. 变量赋值是两边都要紧贴等号。比如"a=1"。如果你想学其它语言那样增加一下可读性,写成了"a = 1”,那等待你的必将是漫长的debug过程。
3. 熟悉条件循环控制语句,函数写法,命令行参数传递等基本语法规则,或者尽可能按照书本或文档中的样例来写。在shell中,for语句和while语句的功能区分很大,一个是对数组遍历,一个是通用的循环控制,后者更常用些。函数的返回值用return传递,但只能传整数,见鬼,还是echo出来,传得更方便些。
4. 数值计算。如果你只需要整数的,用expr就可以了,expr可以简写成类似$((a+b))的形式。如果需要更强大的,那就用bc命令吧。scale=4是设置小数点后保留4位,如果不设依然是整数。bc的返回值如果在(-1, 1) 范围内,通常把小数点前的0省去,这个还是在外部自己加吧。粗略看过bc的文档,发现它已经悄然发展出了一个编程语言的雏形,有各种控制语句,函数等等。bc的作者真是野心勃勃啊!
5. 字符串和正则表达式处理。简单的字符串操作就不提了,可以用shell内置语法来解决。复杂一些的,shell找来了3个帮手:grep, sed, awk。grep只能帮你找到你所需的行,至于更精确的,抱歉。awk的功能强大,但它是基于字段处理的,而且对于我这样的菜鸟来说,也太复杂了些。sed则是一个好帮手,虽然我没有像perl或者python中那样灵活地操纵正则表达式去匹配,但从上面的程序中可以看出,虽然无法提取出匹配的内容,但可以将字符串中不匹配的内容全部替换掉,这也不失为一种曲线救国的策略。用info
sed命令可以看到详细的sed帮助,我建议应尽快定位到它正则表达式的一节,否则sed独具特色的正则表达式编写方法一定会让你焦头烂额的(没有\d, \w,匹配一到多个字符用\+,匹配内容用\( \)圈起来,等等)
6. 执行变量中的命令。如果你的命令是用参数的形式传进来的,它一定是放在一个变量中。你想要执行存放在变量中的命令,比如$command。直接在一行的开头写上$command?,大多数时候可以,但我的经验是如果$command中有对标准输入输出的重定向,重定向的文件会打不开。不论原因如何,解决方案是用eval来执行$command中的命令。这是个不错而优雅的方法。
横观几种脚本语言。perl坚持了它在创建之初对正则表达式和结构化编程的追求,像一位纯洁专业的修道士;python则是在兼具了底层操作与面向对象的特点,号称用对象封装了一切的代码复杂性(以及罪恶),支持代码的无限扩展,像一位野心勃勃的多面手;而shell,则坚持了它简单古板的语法,而且麾下一众小弟(bc,sed,awk等等),像一位古板讲道义的黑道大哥。不管如何,shell脚本是伴随unix和linux成长起来的,使用越多,就越感受到unix的思想(管道,众多的小命令,文本,等等)。如果习惯了shell脚本,相信它编写起来要比其它脚本语言更为简单方便;但如果确实不适应,用perl和python等脚本语言也无妨,关键是更快更好地完成任务。
相关文章推荐
- shell 脚本编写注意事项
- 嵌入式linux入门之编写shell脚本应注意的事项
- shell脚本编写注意事项
- Linux编写shell脚本的注意事项
- Linux系统中编写shell脚本的8个注意事项
- 在Shell脚本中编写AWK脚本实现数据提取—注意格式、awk中使用shell变量、awk中数字字符串转换成数字、awk中字符串相等比较
- Java后台执行Shell脚本, Java方法不用等待Shell程序执行完成并保证关掉Java程序Shell脚本正常运行
- shell脚本的一些注意事项
- shell脚本编写以时间日期命名的文件夹
- C#计算一段程序运行时间的三种方法
- Collection -> .Net集合类型大全,只要灵活运用可以节省很多开发时间和提高程序运行效率
- 编写脚本程序用于监测系统服务http的运行状态
- cygwin安装教程以及使用notepad++编写shell脚本,并通过cygwin运行
- 编写一段程序,运行时向用户提问“你考了多少分?(0~100)”,接受输入后判断其等级并显示出来。判断依据如下:等级={优 (90~100分);良 (80~89分);中 (60~69分);差 (0~59分);}
- ubuntu下编写shell脚本运行 启动goagent
- makefile 中调用shell脚本注意事项
- Shell脚本学习--护眼程序编写心得
- shell脚本命令 运行python文件&python命令行运行python代码
- shell脚本中字符和文件的判断及字符串入参的注意事项
- 编写跨平台Java程序注意事项