资讯动态

【基础IO】-4-再谈重定向

发布时间:2026/10/17 14:30:16 来源:尧图企业网站定制
再谈重定向1、实验中的有趣现象2、重新理解重定向3、直接完成重定向的系统调用——dup4、完善自主shell——加入重定向操作1、实验中的有趣现象还记得我们验证文件描述符fd分配规则的实验吗我们当时是关闭了0号文件流stdin然后新建文件发现新建文件的fd是0。假如我们关闭fd 1的文件呢#includestdio.h#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.hintmain(){close(1);// 关闭stdoutintfdopen(log.txt,O_CREAT|O_WRONLY,0666);printf(log.txt: %d\n,fd);fflush(stdout);close(fd);return0;}本该写入到屏幕上的信息却写入到了新建的log.txt中——这不就是输出重定向吗所以我们通过关闭fd 1的文件再新建文件然后用printf企图向标准输出流中做写入结果内容写入到了文件log.txt从而间接地完成了输出重定向的工作。2、重新理解重定向操作系统是只认文件描述符fd的文件操作相关的系统调用几乎都需要fd的参与。标准输出流stdout底层封装的文件描述符fd是fd 1。未来我们想要对标准输出流做写入默认是写入到文件描述符为fd 1的文件中的。现在关闭了fd 1的文件即关闭了stdout标准输出流。然后我们新建文件log.txt打开log.txt返回的文件描述符就是fd 1。而想要继续向标准输出流做写入的时候由于操作系统只认fd 1的文件流为标准输出流所以就会去打开文件表fd_array中找下标为1的文件结果找到了log.txt。以上就是输出重定向的全过程。输出重定向的本质是做标准输出的时候进程只会写入到文件描述符fd 1的文件当中默认情况下是stdout标准输出流(打开文件表)fd_array中下标为 1 的文件由stdout变为了用户指定的文件。同理追加输出重定向的本质与输出重定向的非常相似。它们的唯一的区别是输出重定向会清空历史内容追加输出重定向不会清空只会在文件内容的末尾继续写入。类似的输入重定向的本质是做标准读取的时候进程只会读取文件描述符fd 0的文件默认情况下是stdin标准输入流(打开文件表)fd_array中下标为 1 的文件由stdin变为了用户指定的文件。3、直接完成重定向的系统调用——dup重定向操作本质上是(打开文件表)fd_array中file*指针的交换即文件描述符fd作为fd_array数组的下标对应的file*指针的交换。如果我们采用上面的“关闭文件再打开”的方式显得比较麻烦。Linux为我们提供了一个操作系统——dup。intdup2(intoldfd,intnewfd);系统调用dup的作用是关闭当前newfd指向的文件将oldfd上的file*指针拷贝给newfd。所以输出重定向可以这样用dup实现#includestdio.h#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.hintmain(intargc,char*argv[]){intfdopen(log.txt,O_CREAT|O_WRONLY|O_TRUNC,0666);// 不追加是O_TRUNC(清空)dup2(fd,1);printf(%s\n,argv[1]);close(fd);return0;}追加输出重定向可以这样用dup实现#includestdio.h#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.hintmain(intargc,char*argv[]){intfdopen(log.txt,O_CREAT|O_WRONLY|O_APPEND,0666);// 追加是O_APPENDdup2(fd,1);printf(%s\n,argv[1]);close(fd);return0;}输入重定向可以这样用dup实现#includestdio.h#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.hintmain(){intfdopen(log.txt,O_RDONLY,0666);dup2(fd,0);inta0;floatf0.0f;charc0;scanf(%d %f %c,a,f,c);printf(%d %f %c\n,a,f,c);close(fd);return0;}学会使用dup之后我们就可以继续完善我们自主实现的shell即加上重定向操作。4、完善自主shell——加入重定向操作注意我们当前加入的重定向操作是只针对普通命令即需要bash创建子进程执行的命令的重定向操作。往自主shell中加入重定向操作需要经过以下两个步骤完善执行命令函数Execute。定义全局的标识符redir_type表示重定向操作的具体选项不重定向#define NONE_REDIR 0输入重定向#define IN_REDIR 1输出重定向#define OUT_REDIR 2追加输出重定向#define NONE_REDIR 3以后在Execute中检测redir_type发生的变化若redir_type 0不做任何操作若redir_type 1检测到需要做输入重定向stdin流换成指定文件流dup2(fd, 0)若redir_type 2检测到需要做输出重定向stdout流换成指定文件流dup2(fd, 1)若redir_type 3检测到需要做追加输出重定向stdout流换成指定文件流dup2(fd, 1)添加函数Redir对从用户方读取的命令字符串做预处理看看字符串中有没有 以设置redir_type定义全局的重定向文件名redir_filename提取字符串中重定向操作符右侧的文件名写入redir_filename// Execute中创建子进程的部分elseif(0id){intfd0;if(IN_REDIRredir_type){fdopen(redir_filename,O_RDONLY);dup2(fd,0);}elseif(OUT_REDIRredir_type){fdopen(redir_filename,O_CREAT|O_WRONLY|O_TRUNC,0666);dup2(fd,1);}elseif(APP_REDIRredir_type){fdopen(redir_filename,O_CREAT|O_WRONLY|O_APPEND,0666);dup2(fd,1);}else{// do nothing}execvp(argv[0],argv);exit(12);}voidBash(){staticchar*env[256];_environenv;LoadEnv();while(1){PrintPrompt();GetCommandLine();if(0strlen(commandline))continue;Redir();// 获取用户输入后先对可能包含的重定向操作做处理再分析命令ParseCommandLine();if(CheckBuildinAndExecute())continue;Execute();}}staticvoidRedir(){char*startcommandline;char*endcommandline[strlen(commandline)1];// 左闭右开while(start!end){if(*start){if(*(start1)){redir_typeAPP_REDIR;*start\0;start2;}else{redir_typeOUT_REDIR;*start\0;start;}Clear_Left_Space(start);// 宏替换的技巧1. 直接写不利于观察。2. 写成函数要传二级指针// 过滤空格找到重定向的文件名redir_filenamestart;break;}elseif(*start){redir_typeIN_REDIR;*start\0;start;Clear_Left_Space(start);redir_filenamestart;break;}else{start;}}}处理重定向操作的函数Redir就很有讲究。首先是寻找重定向操作符设置redir_type。定义char*指针start遍历输入字符串遇到就是输入重定向遇到如果start下一位也是那么就是追加输出重定向否则就只是输出重定向设置完redir_type之后我们可以将重定向操作符改写成\0这样做是对用户输入字符串做“切割”切出操作符之前的命令(这样后续在解析函数ParseCommandLine中strtok遇到\0就会停下)。再然后就要提取redir_filename。我们可以使start跳过操作符继续向后找文件名。而操作符之后可能直接就是文件名也可能是许多前导空格所以我们需要做一个循环操作过滤掉空格直接写循环吗太冗余了代码看起来不太简洁。而且这个循环操作以后可能还要用外部设计成一个函数吗那就要传递二级指针才不会因为传值拷贝而不能对start进行修改这里就涉及到一个编程技巧将此处的循环操作设计成一个宏(替换)。#defineClear_Left_Space(pos)do{\while(isspace(*pos))\pos;\}while(0)宏的本质就是替换代码在预处理阶段就会用这段循环操作替换Clear_Left_Space同时do{}while(0)中套while循环可以防止宏函数直接跟;造成的实际循环未执行的情况发生。当start找到了一个非空的字符意味着找到了文件名。就可以设置redir_filename。添加重定向操作还有一个重要的一点一定要在下一次输入命令之前重置redir_type redir_filename不然会发生意想不到的问题。代码演示

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑