最近写了不少bash脚本,方便开发和调试;不到三个月的时间就累积了二十几个不同功能的小工具;现在小结一下,记录bash编程的一点心得和体会。
一如既往,当我遇到经常需要键入相似的命令组合、反复执行类似的操作的时候,我就会设法写个批处理、自动化的脚本,以减少重复劳动。我们的工作进程一般都预留一个调试控制台端口,可以利用telnet执行自定义的调试指令。比较典型的一个工作场景的操作如下:
user$telnet 127.0.0.1 23
telnet to 127.0.0.1:23, connecting...
escape character is "^]"
>getCustomValue
CustomValue = 0
>setCustomValue 12345
CustomValue = 12345
>^]
telnet>quit
Close connection ...
user$
当需要反复操作,例如想随时查看一些调试信息时,我就会觉得啰嗦,总想偷懒,寻找减少敲击次数的方法。
对于利用脚本执行这种交互式的操作,网上查到的解决办法主要有两大方向:一是通过输入重定向,二是使用如expect等的工具。个人偏好用bash,因此选择了方法一。原始代码如下:
#!/bin/bash
rm -f tmp_in
mknod tmp_in p
exec 8<> tmp_in
telnet 127.0.0.1 23 <&8 &
echo "getCustomValue" >> tmp_in
echo "setCustomValue 12345" >> tmp_in
大概的原理是用mknod tmp_in p建立一个管道文件,作为输入重定向。因此我们可以不断向tmp_in输入字符,传递给telnet,从而执行一个又一个自定义指令。
当然,这个脚本需要完善的地方还有很多,例如ip、端口和命令等内容都写死了,不方便重用;还有就是telnet在后台一直等待tmp_in的输入,在脚本结束后telnet还在后台执行没退出。根据我的需要把脚本改为:
#!/bin/bash
#parse arguments
if [ $# -lt 2 ] ; then
echo "$0 [port] [custom command] ..."
exit
else
port=$1
command="$2 $3 $4 $5 $6 $7 $8 $9"
fi
#redirect input
rm -f tmp_in
mknod tmp_in p
exec 8<> tmp_in
#process custom command
telnet 127.0.0.1 ${port} <&8 &
echo "${command}" >> tmp_in
sleep 1
#quit: escape character ^] ascii is \035
echo -e "\035quit" >> tmp_in
这样,之前的操作可以用./customTelnet.sh 23 getCustomValue
和./customTelnet 23 setCustomValue 12345
两个命令操作代替了。稍微一提的是退出符"^]"
,在命令交互时是按ctrl+]
,查ascii码表可知是0x1D
,即035
,最初我写脚本的时候没认真看echo的说明,忘了加-e
选项,困扰了不少时间;此外是"\035quit“
不能分开两个echo,否则相当于中间插入回车,又回到了telnet的命令交互模式。
这个脚本的真正”威力“在于和其他工作工具协作实现自动化。例如:利用./customTelnet xx | grep xx | awk xx | sed xx
实现对调试信息的进一步处理,或者是作为一个基本命令在另一个脚本里执行:for port in portlist do ./custom ${port} xx
,等等...这样这个脚本可以作为许多业务操作实现复杂功能的基本组件。类似地,凡是像telnet那样有交互式控制台的进程,例如我现在项目需要用到的mysql、ftp等,都可以写脚本进行自动化批处理。
当然,这个脚本可以改进的地方还有很多,例如输入参数的处理,标准输入重定向的临时文件处理,ip地址,延时操作等等。
学习bash,最好的方法是实践,在编写bash脚本的过程中,还需要学习到其他方面的内容,例如awk、sed等常用命令、正则表达式还有linux的系统管理等。不像c/c++,我对bash的语法是很不熟悉,经常是使用的时候再细查,过后可能又会忘掉...此外,现在我复用的基础模块是文件,还没接触过函数,毕竟我的脚本还很简单。期待以后掌握函数之后能发挥bash更大的效能。