浅谈 bash I/O操作 by tay bash的I/O处理是非常重要的一块,这点是毋庸置疑的,它承载着所有的文件操作(读、写)、标准输出/输入流控制等,它使得bash与操作系统的联系更紧密,使脚本程序更加简练、高效、安全。
然而bash的I/O操作往往成了蒙蔽他人双眼的迷雾,这种神秘让很多bash脚本程序对其敬而远之, 的确,脚本程序的魅力之一在于“开发快速、浅显易懂”,高级的I/O使用势必会打破这一点,造成程序的可读性“差”
直观的认识
人总是最容易理解直观的东西,所以shell的设计者们这样做了
例子1: [root@tay test]# echo ABC >txt
“这样字符串ABC就被输出到txt文件中了”,这样的说法很易懂,或者通俗的:“这样ABC就被送到txt中了”,因为 “>”很形象,同样的:
例子2: [root@tay test]# sendmail -t <tmp.txt
“<”作为输入,这也很形象,还有:
例子3: [root@tay test]# ps ax | grep resin
“|” 管道符,先不用看样子,听名字就能猜个差不多了
这些都是很常用的,而这直观的认识对于所有人都那样的容易理解,何乐而不为呢? 而对于一个深爱bash的人来说,这种嘲弄如何容忍?
Dirty work
bash dirty work ! 可以理解为:“低效的(开发速度或运行速度)”、“丑陋的”或“幼稚的”的bash脚本程序 从很多地方能看出一个脚本是不是dirty work,比如bash的I/O操作
1、用“here document” 、“进程替换”或“文件描述符”技巧来避免无用的临时文件
可能是本人对linux操作系统的临时文件有过度的洁癖吧 ! 如果那些临时文件没有用,就没有理由生成它, 除了低效(I/O读写频繁)、低可读性以外,它毫无意义 试想,一旦文件系统只读了,或文字大于100行,用临时文件更是不合适的
例如: echo ... >tmp.txt echo ... >>tmp.txt cat txt >>tmp.txt sendmail -t< tmp.txt rm -f tmp.txt
再如: awk ’{print $1,$2,$3}’ list.conf >tmp.txt while read i j k;do ... done <tmp.txt rm -f tmp.txt
这些tmp.txt文件用完了还要删除,或用完了就无用了,为何要生成它?
第一个例子用here document来解决: sendmail -t <<-Endmail ... ... `cat txt` Endmail 因为用的是 “<<-”,所以里面的每行首是tab,不是空格, 这样看起来是不是更好看了呢?:) 更重要的是,将here document直接作为sendmail的stdin,免去了生成临时文件,更高效,也更干爽 第二个例子用“进程替换”来做: while read i j k;do ... done < <(awk ’{print $1,$2,$3}’ list.conf) 将stdin替换成一个进程,也避免了生成临时文件
BTW: 你可能会说用管道符: awk ’{print $1,$2,$3}’ list.conf | while read i j k;do ... 不是也行吗,免了临时文件? 但这样会新fork出两个subshell, 效率很低不说,很多变量更都成了subshell的私有变量,不易调用
第二个例子用“文件描述符”+ “进程替换”的另一种方法: exec 6< <(awk ’{print $1,$2,$3}’ list.conf) while read -u6 i j k;do ... done exec 6<&- 新建fd6,read命令从fd6中取到stdin,效率和原理上,和单单用“进程替换”是一样的 但是这样可以解决另一个问题 --- 文件描述符冲突
什么时候会冲突呢? 比如while read 结构中,遇到ssh等需要重新建立stdin的程序时,就会出问题了, 循环只执行一遍就会因为read的stdin被重置,read返回false,while就停止了 所以,当while read遇到ssh时,应该这样, read不用stdin(fd0),而用fd6或其他的fd,来避免冲突: exec 6< <(cmd1) while read -u6 i;do ssh 127.0.0.1 "cmd2" ... done exec 6<&- 当然只是举个例子,可以直接用ssh的“-n”参数来关闭ssh的stdin来避免这种情况: while read i;do ssh -n 127.0.0.1 "cmd" ... done 2、用“code块(大括号)”或“文件描述符”来生成文件
当我们要生成多行文本文件的时候,比如生成日志等等,这样做是很糟的: echo "aaa" >tmp.log echo "bbb" >>tmp.log echo "ccc" >>tmp.log cat txt >>tmp.log 这样很多行的“>>”,很是无味儿 :( 多次I/O写操作、糟糕的可读性,没有理由不用下面这种方法,“code块”: { echo "aaa" echo "bbb" echo "ccc" cat txt } >tmp.log 这样多次的写操作就变成了一次,而且代码干爽
最后来看一个不是很常见的的I/O操作,来实现同样的功能,用“文件描述符”: exec 6>&1 exec >&tmp.log
echo aaa echo bbb echo ccc
cat txt
exec 1>&6 exec 6>&- 其中,将tmp.log临时变成stdout,然后再恢复stdout,这种做法更像其他语言的“文件句柄”,可能更会被接受 本文原创自无线技术运营空间: http://wireless.qzone.qq.com 及 http://blog.youkuaiyun.com/wireless_tech (专注无线技术运营——无线技术(操作系统/数据库/WEB前端/负载均衡/系统容灾/系统安全/短信接入/WAP接入/3G等)、无线业务运营、无线开放平台、统计分析(用户行为分析/数据挖掘)、CP合作,联系我们:1780551083@qq.com) |