所有的Unix I/O重定向都是基于标准数据流的原理。考虑下Unix中【sort】工具是如何工作的。【sort】从一个数据流中读取字节,再将结果输出到另外一个流中,同时若有错误产生,则将错误报告给第三个流。如果忽略这写标准流的去向,【sort】工具的基本原型就如图1所示。三个数据流分别如下:
(1)标准输入--需要处理的数据流;
(2)标准输出--结果数据流;
(3)标准错误输出--错误消息流;
_图1
_图2
所有的Unix工具都是使用图1中所示的三种流的模型。此模型通过一个简单的规则来实现。这三种流的每一种都是一个特别的文件描述符,其细节如图2所示。通常通过【shell】命令行运行Unix系统工具时,【stdin】【stdout】和【stderr】连接在终端。因此,工具从磁盘都去数据并且将数据和错误消息写到屏幕。举个例子来说,如果输入【sort】并按下回车键,终端将会被连接到【sort】工具上。随着输入几行文字,当按Ctrol-D键时,【sort】程序将对输入进行排序并将结果输出到【stdout】。大部分的Unix工具处理从文件或标准输入读入的数据。如果在命令行上给出了文件名,工具将从文件读取数据,若无文件名,程序则从标准输入读取数据。从另一方面讲,大部分的程序都不接受输出文件名,它们总是将结果输出到【stdout】或者【stderr】。如果希望改变进程的输出,那么就必须重定向文件描述符。
下面我将Linux中三种将【stdin】重定向到文件的方法与大家分享。
【方法一】:【close】-【open】
第一种方法是close-then-open策略。开始的时候,系统采用经典的设置。即将三种标准流连接到终端上,如图3所示。
接下来,第一步是close(0),即将标准输入流关闭。如图4所示。
最后,使用open(filename,mode)打开一个想连接到【stdin】的文件,如图5所示。(这里解释下,为什么打开i的文件会自动与【stdin】,因为Unix中产生新的文件描述符使用‘最低可用文件描述符’的原则,当前最低的是0号位,即【stdin】)
_图3
_图4
_图5
下面该方式的代码:
/* stdinredir1.c
*purpose: show how to redirect standard input by replacing file
* descriptor 0 with a connection to a file.
* action: reads three lines from standard input, then
* closes fd 0, opens a disk file, then reads in
* three more lines from standard input
*/
#include
#include
main()
{
intfd ;
charline[100];
/* read and print three lines */
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
/* redirect input */
close(0);
fd = open("/etc/passwd", O_RDONLY);
if ( fd != 0 ){
fprintf(stderr,"Could not open data as fd 0\n");
exit(1);
}
/* read and print three lines */
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
}
【方法二】:【open】-【close】-【dup】-【close】
如下图6所示,Unix系统调用dup建立指向已经存在的文件描述符的第二个连接。这种方法需要4个步骤。
(1)open(file):第一步是打开【stdin】将要重定向的文件。这个调用返回一个文件描述符,这个描述符并不是0,而是最低可用文件描述符。
(2)close(0):下一步是将文件描述符0关闭。
(3)dup(fd):系统调用dup(fd)将文件描述符fd复制一个。此次复制使用最低可用文件描述符号。因此,获得的文件描述符是0.
(4)close(fd):最后关闭fd,只留下文件描述符0与文件连接。
_图6
下面是实现此方法的代码:
/* stdinredir2.c
* shows two more methods for redirecting standard input
*use #define to set one or the other
*/
#include
#include
/* #defineCLOSE_DUP/* open, close, dup, close */
/* #defineUSE_DUP2/* open, dup2, close */
main()
{
intfd ;
intnewfd;
charline[100];
/* read and print three lines */
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
/* redirect input */
fd = open("data", O_RDONLY);/* open the disk file*/
#ifdef CLOSE_DUP
close(0);
newfd = dup(fd);/* copy open fd to 0*/
#else
newfd = dup2(fd,0);/* close 0, dup fd to 0 */
#endif
if ( newfd != 0 ){
fprintf(stderr,"Could not duplicate fd to 0\n");
exit(1);
}
close(fd);/* close original fd*/
/* read and print three lines */
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
fgets( line, 100, stdin ); printf("%s", line );
}
【方法三】:【open】-【dup2】-【close】。
程序srdinredir2.c包含了条件编译#ifdef,系统调用dup2(fd,0)来替换close(0)和dup(fd).
方法三的代码已包含在方法二中。
这些例子显示了程序如何将标准输入重定向到文件,实际上,如果程序希望读取文件,直接打开文件就可以了,根本不需要重定向。这些例子的真正意义在于说明一个程序如何将标准输入重定向到别的程序。