【经验总结】浅谈 bash I/O操作

来源:互联网 发布:网络存储工程师待遇 编辑:程序博客网 时间:2024/06/05 10:42

 

浅谈 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.csdn.net/wireless_tech (专注无线技术运营——无线技术(操作系统/数据库/WEB前端/负载均衡/系统容灾/系统安全/短信接入/WAP接入/3G等)、无线业务运营、无线开放平台、统计分析(用户行为分析/数据挖掘)、CP合作,联系我们:1780551083@qq.com)

原创粉丝点击