已打开的文件描述符在fork和exec调用后保留下来,我们可以利用对进程这方面知识点的理解来改变程序的行为。
这个例子涉及一个过滤程序:它从标准输入读取数据,然后向标准输出写数据,同时在输入和输出之间对数据做些有用的转换。
过滤程序upper.c
#include<stdio.h> #include<stdlib.h> #include<ctype.h> int main() { int ch; while( EOF != (ch=getchar())) { putchar(toupper(ch)); } exit(0); }
upper.c执行效果:
如果我们利用shell从定向把一个文件的内容全部转为大写,如下所示:
如果我们想在另一个程序中使用这个过滤程序会发生什么呢?下一个例子useupper.c接受一个文件名做命令行参数,若果对它调用不正确,他将会响应一个错误信息。
#include<unistd.h> #include<stdio.h> #include<stdlib.h> int main(int argc,char *argv[]) { char *filename; int i; if(argc != 2) { fprintf(stderr,"usage :useupper file "); exit(1); } filename = argv[1];
//重新打开标准输入,并再次检查有无错误发生,然后用execl调用upper程序。 for (i = 0;i<argc;i++) { printf("the argv[%d] is %s ",i,argv[i]); } if(!freopen(filename,"r",stdin)) { fprintf(stderr,"can't redirect stdin from file %s! ",filename); exit(2); } execl("./upper","upper",0); //execl会替换当前的进程。如果没有发生错误,剩下的语句不会执行。 perror("could not exec ./upper"); exit(3); }
分析一下这个程序的流程:
运行时我们应该给他提供一个文件,让他把文件的内容全部转换为大写。这项工作由upper程序完成,但它不会处理文件名参数。注意,我们不需要upper程序的源代码。我们可以利用这种方法
运行任何可执行程序。
useupper程序用freopen函数先关闭标准输入,然后将文件流stdin与程序参数给定的文件名关联起来。接下来,它调用execl用upper程序替换掉正在运行的进程代码。有hi那我IE已打开的文件描述符会在execl调用
之后保留下来,所以upper程序的运行情况和他在shell提示符下运行情况完全一致。
ps:
1.
stdout(Standardoutput)标准输出
stdin(Standardinput)标准输入
stderr(Standarderror)标准错误
2.int fprintf (FILE* stream, const char*format, [argument])
FILE*stream:文件指针
const char* format:输出格式
argument :附加参数列表
作用:
fprintf( )会根据参数format 字符串来转换并格式化数据, 然后将结果输出到参数stream 指定的文件中, 直到出现字符串结束(' ')为止。
3.FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream);
freopen是被包含于C标准库头文件<stdio.h>中的一个函数,用于重定向输入输出流。该函数可以在不改变代码原貌的情况下改变输入输出环境,但使用时应当保证流是可靠的。
形参说明:
filename:需要重定向到的文件名或文件路径。
mode:代表文件访问权限的字符串。例如,"r"表示“只读访问”、"w"表示“只写访问”、"a"表示“追加写入”。
stream:需要被重定向的文件流。
返回值:如果成功,则返回该指向该输出流的文件指针,否则返回为NULL。
![](https://images2015.cnblogs.com/blog/726258/201702/726258-20170224224124320-1032293034.png)
4.perror()
perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno(这里的说法不准确,errno是一个宏,该宏返回左值) 的值来决定要输出的字符串。
在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了errno的值。perror函数只是将你输入的一些信息和现在的errno所对应的错误一起输出。