标准IO
stdin,stdout,stderr,分别是标准输入,标准输出,标准错误。windows和*nix下都有的。
默认情况下,标准输入相当于从键盘输入,标准输出和标准错误相当于向屏幕输出。
C语言中,这三个是FILE*类型(文件指针),例如你用printf,那么内容直接输出到标准输出,等价于fprintf(stdout,...);你用scanf,直接从标准输入读取,等价于fscanf(stdin,...)。
它们有什么用呢?对windows来说,好像只能用来写实验性的小程序。
实际上,*nix中,它们是用户界面的重要组成部分,另外,还可以用来做重定向。
IO重定向
重定向的基本用法,在*nix的shell中如下使用:
~/test $echo Hello Hello ~/test $echo Hello >a.txt ~/test $cat a.txt Hello ~/test $
这里echo命令用来原样输出,>号把标准输出重定向到文件,随后用cat命令显示那个文件的内容。
再看一个重定向标准输入的例子:
~/test $wc <a.txt 1 1 6 ~/test $
这里用<号把文件的内容送给wc程序去统计字符数
管道
前面shell中的的输入输出重定向都是只限于文件,管道则提供一种通用的方式。
~/test $cat a.txt | wc 1 1 6 ~/test $
这里管道符|把两个进程的输出和输入连接起来了。同样实现了统计字符数的目的。
类似的用法,在windows的cmd中也一样的,用兴趣的同学可以去发掘一下。
但是这两者之间有一个重大的区别在于,*nix下有一种软件模式,叫做过滤器,是windows软件所不具备的。
过滤器
正是由于过滤器模式的存在,使得*nix可以用大量小工具集合,做到“一行shell胜过万行C”。
过滤器是指这样一种程序行为,它顺序读取命令行指定的一个或多个文件,如果没有指定文件名,则从标准输入读取,然后把处理结果写入标准输出,如果这中间有任何错误发生,则把错误写入标准错误中。
从上面的描述,我们很容易想象,多个遵循过滤器模式的程序可以用管道符串成一个链式处理。信息的传递通过标准输入和标准输出来传递,而错误信息则送到标准错误中,以免干扰到真正的信息流。
日常经常用到的通用目的过滤器有下面这些:
cat 把输入拷贝到输出
head 截取开始数行
tail 截取结尾数行
cut 截取列
grep 执行按行搜索功能
wc 执行计数功能
sort 执行排序
一些利用过滤器的实例(这些例子大家可以分段去用,自然就知道每一部分含义):
ps -ef | grep httpd | grep -v grep | wc -l
输出当前系统中httpd进程的数目
cut -d' ' -f 1 access.log | sort | uniq -c|sort -nr
从apache日志文件中截取一列,并按照出现次数排序
即使不是严格遵循过滤器模式,只要程序有着规范的标准输出,仍然可以用做管道的起始位置。并且,它也可以被其他程序轻松的复用。
举个例子,windows下要获取文件列表,只好用操作系统提供的API,或者C库函数。
但是*nix下可以直接调用ls命令获取结果
$ls -FLa >~/dir.log $cat ~/dir.log ./ ../ bin/ boot/ dev/ etc/ home/ lib/ lost+found/ mnt/ opt/ proc/ root/ sbin/ sys/ tmp/ tony.tgz usr/ var/ $
作为一个反例,我们看看windows中的dir命令是怎么设计的
C:/>dir 驱动器 C 中的卷没有标签。 卷的序列号是 B0B5-18AD C:/ 的目录 2009-12-21 12:47 <DIR> 360Rec 2004-09-08 10:54 0 AUTOEXEC.BAT 2004-09-08 10:54 0 CONFIG.SYS 2009-11-18 09:19 <DIR> dell 2006-06-22 17:27 <DIR> Documents and Settings 2009-11-17 14:04 <DIR> Downloads 2009-11-17 17:44 <DIR> i386 2009-07-07 14:31 4,128 INFCACHE.1 2009-12-29 17:37 <DIR> Program Files 2009-07-07 14:37 <DIR> Temp 2009-12-21 12:31 <DIR> WINDOWS 3 个文件 4,128 字节 8 个目录 9,779,306,496 可用字节 C:/>
这样的结果,要从中提取出一个文件列表来,非常的困难,其难度远大于使用API或者库函数。(dir /w 的结果看起来好一点,但是它的问题更多)
应该说,从设计之初,windows的命令行工具就没想要与其他程序交互。
管道在程序中的应用
前面我们看到,输入输出不仅仅可以重定向到文件,它还可以通过管道传递到其他进程中去,借助这个特性,我们可以让它重定向到网络,用C写过CGI的应该可以理解这一点,设想一下,我们不需要socket,仅仅用scanf,printf等等标准IO函数,就可以实现一个网络协议的服务端,而网络连接的实际建立,是通过通用的网络服务程序来建立的。例如tcpd建立通用的基于tcp的网络服务,每当有连接进来,tcpd启动你的程序,它接收到得数据将写入你的标准输入,而你的标准输出将被tcpd发送到客户端。
类似的开发思想,我们可以不用修改任何代码,就为你的服务器程序增加SSL的支持。
管道在windows中也有实现,但是由于开发氛围,或者其他技术原因,应用的不是很普遍。