Linux——文件重定向

目录

前言

一、重定向 

二、重定向的运用

三、dup2

四、命令行中的重定向

五、为什么要有标准错误


前言

在之前我们学习了文件标识符,直到close可以使用文件标识符进行关闭,但是当我们关闭1号(stdout)时,无法往显示器中打印数据,这都是文件的重定向在起作用,今天我们来一探究竟。

一、重定向 

大家看如下代码,关闭了fd:1(stdout),根据之前的学习,后面打开的 log1.txt 的fd就会设为1。fflush(stdout)我们跟缓冲区有关,我们暂时忽略。那么无法往显示器上打印数据,数据去哪里了呢?

 看到结果,竟然往log1.txt上面打印了

这是因为printf()函数,只认文件描述符1,他只会往文件描述符1号上面打印,之前 1 为显示器,于是往显示器上打印,现在显示器被关闭,1号变成了文件,那么printf也会往1号进行打印,也就打印到了文件中。

而这就是重定向,这里是输出重定向重定向的本质就是修改特定文件fd的下表里的内容。也就是以前指向原文件,后面指向新文件。

其中有一句代码fflush(stdout);很重要,如果我们不进行刷新缓冲区,那么也不会往log1.txt里面写入数据。这是C语言给我们提供的缓冲区,他会将我们打印的内容刷新到对应的fd文件中。因为我们后续会close(fd)文件,后续从C语言提供的缓冲区中把数据刷新到fd的时候,fd已经关闭,也就无法刷新,因此这里需要fflush进行刷新再关闭。

二、重定向的运用

那么根据输出重定向,追加重定向很很简单了,O_TRUNC换成O_APPEND就行。

那我们再看看输入重定向,我们使用 fread+重定向去读数据。

fread第一个参数为读取的数据放到哪里,第二个参数为读取单个单元的大小,第三个参数为读取多少单元,第四个参数是从哪里读。

我们使用如下代码,关闭0号文件,那么打开的log1.txt的fd就会被设为0,后面fread读取的时候,不再会从键盘中读取,而是直接冲log1.txt中读取,依然是老样子,fread认的是文件标识符。 

那么结果也是能够预料到的,fread从文件中读取了数据。这就是输入重定向

重定向的基本原理:上层fd不变,底层fd指向的内容发生改变,是文件描述表级别的数组里的内容的拷贝。

比如log.txt文件发现 1 号文件描述符无内容,他就直接拷贝到1号文件描述符。

三、dup2

但是每次都要用close关闭某个文件这确实有点戳,我们可以使用dup2来进行文件描述符表级别的数组里内容的拷贝。

第一个参数为oldfd,第二个参数为newfd,这局代码会让oldfd进行覆盖newfd(oldfd被保留下来)。这样文件描述符表数组就会有两个指针指向同一文件了。

当我们使用某个fd进行关闭文件时,并不会影响另一个fd,这涉及到引用计数,也就是有几个指针指向,count就为几,某个指针取消指向,使用close进行释放,只会让count-1,直到count为0才会释放。 

 如下代码,不再close,而是dup2,依然可以达到效果

四、命令行中的重定向

我们知道命令行中>为输入重定向,>>追加重定向,<输出重定向

我们拿出之前写的简易版myshell再添加上重定向

简易版myshell代码如下

  #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#define NUM 1024#define SIZE 64char* getUsername(){char* env = getenv("USER");if(env) return env;return NULL;}char* getHostname(){char* env = getenv("HOSTNAME");if(env) return env;return NULL;}char* getPwd(){char* env = getenv("PWD");if(env) return env;return NULL;}int main(){while(1){char command[NUM];char* argv[SIZE];int argc = 0;printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd());//打印fgets(command,NUM,stdin);  //输入完成后还会输入回车,导致换行command[strlen(command)-1] = '\0';argv[argc++] = strtok(command," ");while(argv[argc++] = strtok(NULL, " "));pid_t id = fork();if(id == 0){//childexecvp(argv[0],argv);exit(1);}else{pid_t rid = waitpid(id,NULL,0);if(rid>0) printf("等待成功\n");}}}

首先宏定义一下代表各种重定向,定义一个全局变量,文件名也设置一下 

在我们显示基本消息并输入命令后,command获取到了命令字符串,此时我们就需要判断是否发生重定向

总代码如下

  #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#define NUM 1024#define SIZE 64#include<ctype.h>    #include<sys/stat.h>    #include<fcntl.h>    #define NUM 1024    #define SIZE 64    #define NoneRedir 0    #define OutputRedir 1    #define AppendRedir 2    #define InputRedir 3    int redir = NoneRedir;    char* filename = NULL; char* getUsername(){char* env = getenv("USER");if(env) return env;return NULL;}char* getHostname(){char* env = getenv("HOSTNAME");if(env) return env;return NULL;}char* getPwd(){char* env = getenv("PWD");if(env) return env;return NULL;}#define SkipSpace(filename) do{ while(isspace(*filename)) filename++; }while(0)void checkRedir(char command[],int len){char* end = command + len -1;char* start = command;while(end>=start){// ls -al > log.txtif(*end == '>'){//ls -al >> log.txtif((*end-1)=='>'){*(end-1) = '\0';filename = end+1;SkipSpace(filename);redir = AppendRedir;                            break;}else{*end = '\0';filename = end + 1;SkipSpace(filename);redir = OutputRedir;break;}}else if(*end == '<'){*end = '\0';filename = end+1;SkipSpace(filename);redir = InputRedir;break;}else{end--;}}}int main(){while(1){redir = NoneRedir;                                                                          filename = NULL;//显示消息并输入char command[NUM];char* argv[SIZE];int argc = 0;printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd());//打印fgets(command,NUM,stdin);  //输入完成后还会输入回车,导致换行command[strlen(command)-1] = '\0';//判断是否发生重定向checkRedir(command,strlen(command));//分割字符串argv[argc++] = strtok(command," ");while(argv[argc++] = strtok(NULL, " "));pid_t id = fork();if(id == 0){//childint fd = 0;if(redir == InputRedir){fd = open(filename,O_RDONLY);dup2(fd,0);}else if(redir == OutputRedir){fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC, 0666); dup2(fd,1);}else if(redir == AppendRedir){fd = open(filename,O_WRONLY | O_CREAT | O_APPEND, 0666); dup2(fd,1);}execvp(argv[0],argv);exit(1);}else{pid_t rid = waitpid(id,NULL,0);if(rid>0) {};}}}

我们的代码中,先进行的重定向,再进行了程序替换,这并不会影响,因为是进程的PCB中的指针指向的文件结构体,文件结构体中有文件标识符表,文件标识符表进行了重定向。

而程序替换也是进程PCB中的一个指针指向的虚拟内存空间,通过页表映射到物理内存,程序替换是物理内存代码与数据的覆盖,并不会改变进程(pid也不会变,依然是原来的进程)。那就也不必提PCB里面的内容了。

五、为什么要有标准错误

我们之前的重定向,一直在重定向0号(stdin)和1号(stdout),为什么还要有2号(stderr)呢?

大家看如下代码,打印结果也符合预期,都是往显示器上打印。 

 可是当我们将执行内容重定向到log.txt时,发现hello stderr没有被重定向,而是仍然输出在屏幕上

这是因为我们在进行输出重定向的时候,将1号文件标识符区域进行了覆盖,不管2号文件描述符什么事情,因此stderr老样子,维持不动。 

如果我们非要将标准输出和标准错误的内容都放到log.txt里,则需要在后面添加 2>&1 。这句代码的意思是将&1里面的内容放到2号文件描述符里,由于1号文件描述符已经被 log.txt 覆盖了,因此2好描述符相当于也被 log.txt 覆盖,也就都打印到了log.txt里面

为什么这这样写可以呢?因为 ./myfile > log.txt 其实上是 ./myfile 1 > log.txt 。代表将log.txt里的内容放到1号文件描述符中。2>&1 也是同理,1号地址的内容放到2号

这样设计的目的,是让一个程序在运行时,将标准输出和标注错误放到不同的文件中,方便我们排查错误,如下就打印到了分别的两个文件中。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/525460.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Thingsboard学习杂记

知识杂记 1.遵循磁盘绑定的内存数据库和遵循磁盘支持的内存数据库 遵循磁盘绑定的内存数据库和遵循磁盘支持的内存数据库有不同的工作方式&#xff0c;它们的优点和缺点也不同。 遵循磁盘绑定的内存数据库的优点&#xff1a; 数据库可以支持更大的数据集合&#xff0c;因为数…

springboot整合shiro的实战教程(一)

文章目录 1.权限的管理1.1 什么是权限管理1.2 什么是身份认证1.3 什么是授权 2.什么是shiro3.shiro的核心架构3.1 Subject3.2 SecurityManager3.3 Authenticator3.4 Authorizer3.5 Realm3.6 SessionManager3.7 SessionDAO3.8 CacheManager3.9 Cryptography 4. shiro中的认证4.1…

贝叶斯优化CNN-LSTM回归预测(matlab代码)

贝叶斯优化CNN-LSTM回归预测matlab代码 贝叶斯优化方法则采用贝叶斯思想&#xff0c;通过不断探索各种参数组合的结果&#xff0c;根据已有信息计算期望值&#xff0c;并选择期望值最大的组合作为最佳策略&#xff0c;从而在尽可能少的实验次数下达到最优解。 数据为Excel股票…

JS直接量及其相关对象

什么是直接量 直接量是指不需要创建对象就可以直接使用的变量。ES中的直接量主要有三种类型&#xff1a;表示字符串的string类型、表示数字的number类型和表示true/false的boolean类型。当我们直接将值赋给变量后&#xff0c;ES就会自动判断其类型&#xff0c;而且当参数发生变…

2024038期传足14场胜负前瞻

2024038期售止时间为3月10日&#xff08;周日&#xff09;20点30分&#xff0c;敬请留意&#xff1a; 本期深盘多&#xff0c;1.5以下赔率3场&#xff0c;1.5-2.0赔率2场&#xff0c;其他场次是平半盘、平盘。本期14场整体难度中等偏上。以下为基础盘前瞻&#xff0c;大家可根据…

SpringCloud 各自组件的停更/升级/替换

一、停更不停用 现在 SpringCloud 不再修复 bug&#xff0c;也不再接收合并请求&#xff0c;也不再发布新版本&#xff0c;但是目前还是可以继续使用的。 二、以前的组件 以前 SpringCloud 常用的组件如下图&#xff0c;服务的注册和发现使用 Eureka&#xff0c;服务的负载和调…

民宿租赁系统全栈开发:Java+SpringBoot+Vue+MySQL

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

三甲医院体检科PEIS系统全套源码,可实现健康体检、职业病体检、从业人员体检、妇女儿童体检、外检服务等全方位体检服务

大型体检中心、三甲医院体检科的PEIS系统源码&#xff0c;体检信息系统源码 一套应用于大型体检中心、三甲医院体检科的PEIS系统源码&#xff0c;C#语言开发实现全流程管理。系统从检前&#xff0c;检中&#xff0c;检后整个业务流程提供标准化以及精细化的解决方案。 实现体检…

Springboot进行web开发

创建springboot工程&#xff0c;基于2022版idea pom.xml文件中的插件爆红&#xff1a; 解决方法&#xff1a;给插件加<version>版本号</version> 版本号和<parent></parent>中的版本号一样。 另外有人说重启也可以解决爆红&#xff0c;可以试一下&a…

YOLO水稻虫害识别数据集(16类,近一万张图像)

YOLO水稻虫害识别数据集&#xff0c;包含稻秆蝇、二化螟、褐飞虱、蓟马、蛴螬、蝼蛄等常见害虫&#xff0c;共16个水稻害虫类别&#xff0c;9700多张图像&#xff0c;yolo标注完整&#xff0c;全部原始数据&#xff0c;未应用增强。 适用于CV项目&#xff0c;毕设&#xff0c;科…

ElasticSearch 底层读写原理

ElasticSearch 底层读写原理 ​ 写请求是写入 primary shard&#xff0c;然后同步给所有的 replica shard&#xff1b;读请求可以从 primary shard 或 replica shard 读取&#xff0c;采用的是随机轮询算法。 1、ES写入数据的过程 1.选择任意一个DataNode发送请求&#xff0c…

UE4.27_ParticleSystem(没写完的材料)

UE4.27_ParticleSystem&#xff08;没写完的材料&#xff09; 参考实例&#xff1a; UE4[蓝图]下雪效果及雪的材质的实现