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.mzph.cn/news/732757.shtml

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

相关文章

00在linux环境下搭建stm32开发环境

文章目录 前言一、环境搭建1.arm-none-eabi-gcc2.openocd 三、创建stm32标准库工程1.创建工程目录2.修改stm32_flash.ld文件3.写makefile文件4.修改core_cm3.c5.写main函数并下载到板子上 最后 前言 我在那天终于说服自己将系统换成了linux系统了&#xff0c;当换成了linux系统…

如何清除keep-alive缓存

在 Vue.js 中&#xff0c;使用 <keep-alive> 组件可以将组件保留在内存中&#xff0c;以避免重复渲染和销毁&#xff0c;从而提高性能。如果需要手动清除 <keep-alive> 组件的缓存&#xff0c;可以通过两种方法来实现&#xff1a; 通过 $destroy 方法销毁组件&…

UE5.1_使用技巧(常更)

UE5.1_使用技巧&#xff08;常更&#xff09; 1. 清除所有断点 运行时忘记蓝图中的断点可能会出现运行错误的可能&#xff0c;务必运行是排除一切断点&#xff0c;逐个排查也是办法&#xff0c;但是在事件函数多的情况下会很复杂且慢节奏&#xff0c;学会一次性清除所有很有必…

JavaWeb--Mybatis

一&#xff1a;Mybatis概述 1.Mybatis概念 MyBatis 是一款优秀的 持久层框架 &#xff0c;用于简化 JDBC 开发&#xff1b; MyBatis 本是 Apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code&#xff0c;并且改名为 MyB…

OpenTenBase 开发环境搭建及Debug设置

最近有个 OpenTenBase开源核心贡献挑战赛 领导建议大家都去试试&#xff0c;我也去凑了下热闹&#xff0c;发现能力有限一时半会是搞不明白了&#xff0c;最多也就是能搞搞文档翻译&#xff0c;或者写点操作手册啥的。 不过不管怎么样&#xff0c;先把开发环境搭上&#xff0c;…

R语言的数据类型与数据结构:向量、列表、矩阵、数据框及操作方法

R语言的数据类型与数据结构&#xff1a;向量、列表、矩阵、数据框及操作方法 介绍向量列表矩阵数据框 介绍 R语言拥有丰富的数据类型和数据结构&#xff0c;以满足各类数据处理和分析的需求。本文将分享R语言中的数据类型&#xff0c;包括向量、列表、矩阵、数据框等&#xff…

【深度学习模型】6_3 语言模型数据集

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 6.3 语言模型数据集&#xff08;周杰伦专辑歌词&#xff09; 本节将介绍如何预处理一个语言模型数据集&#xff0c;并将其转换成字符级…

vue组件之间通信方式汇总

方式1&#xff1a;props和$emit props和$emit仅仅限制在父子组件中使用 1.props&#xff1a;父组件向子组件传递数据 1.1 代码展示 <template><div><!-- 这是父组件 --><div>父组件中的基本数据类型age的值是:{{this.age}}</div><div>…

giffgaff怎么充值?giffgaff怎么续费?

-性价比高&#xff1a;0月租&#xff0c;免费接收短信&#xff0c;充值一次&#xff0c;接码可以用20年以上&#xff08;仅需半年保号一次&#xff09;&#xff0c;可能是国内性价比最高的接码实体卡&#xff01;-安全&#xff1a;实体卡无须担心因号码被风控&#xff0c;还可以…

springboot+vue,上传图片,回显,以及报错404的问题

最近遇到一个问题&#xff0c;上传图片到服务器以后&#xff0c;回显不了&#xff0c;报错404&#xff1b; 历时三天终于找到解决办法&#xff1a; 1.后端代码&#xff1a; RestController RequestMapping("file") SuppressWarnings({"unchecked","…

面试经典150题【61-70】

文章目录 面试经典150题【61-70】61.旋转链表86.分隔链表104. 二叉树的最大深度100.相同的树226.翻转二叉树101.对称二叉树105.从前序与中序遍历序列构造二叉树106.从后序和中序遍历序列构造二叉树117.填充每个节点的下一个右侧节点指针II114.二叉树展开为链表 面试经典150题【…

留学|谈PS|耶鲁大学管理学院招生办执行主任谈个人PS

仅仅为了共同学习与分享 内容提要:在一篇个人自述中&#xff0c;我们要求申请人将他们的过去和未来有机地结合起来。我们把这种自传叫作“职业目标”自传。它为我们提供了确凿的信息&#xff0c;同时也可以确定申请人是否具有对自己及其职业的综合性反思能力。通常的情况是&am…

PostgreSQL 流复制

文章目录 1.流复制介绍2.异步流复制2.1.主库部署2.2.备库部署2.3.测试 3.同步复制3.1.主库部署3.2.备库部署3.3.测试 4.主备切换 开源中间件 # PostgreSQLhttps://iothub.org.cn/docs/middleware/ https://iothub.org.cn/docs/middleware/postgresql/postgres-stream/1.流复制…

Linux运维_Bash脚本_编译安装Mesa-23.3.6(OpenGL)

Linux运维_Bash脚本_编译安装Mesa-23.3.6(OpenGL) Bash (Bourne Again Shell) 是一个解释器&#xff0c;负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件&#xff0c;并于 1989 年发布的免费软件&#xff0c;作为 Sh (Bourne Shell) 的替代品。 您可以在…

为什么main方法在Java中代表主线程?

main 方法在 Java 等编程语言中确实代表着程序的入口点&#xff0c;也就是程序开始执行的地方。当我们启动一个 Java 应用程序时&#xff0c;JVM&#xff08;Java 虚拟机&#xff09;会首先查找 main 方法&#xff0c;并从那里开始执行程序。 关于为什么 main 方法代表主线程&a…

unity学习(53)——选择角色界面--分配服务器返回的信息

好久没写客户端了&#xff0c;一上手还不太适应 1.经过测试&#xff0c;成功登陆后&#xff0c;客户端请求list_request&#xff0c;成功返回&#xff0c;如下图&#xff1a; 可见此时model第三个位置的参数是1.也成功返回了所有已注册角色的信息。 2.之前已知创建的角色信息…

141 Linux 系统编程18 ,线程,线程实现原理,ps –Lf 进程 查看

一 线程概念 什么是线程 LWP&#xff1a;light weight process 轻量级的进程&#xff0c;本质仍是进程(在Linux环境下) 进程&#xff1a;独立地址空间&#xff0c;拥有PCB 线程&#xff1a;有独立的PCB&#xff0c;但没有独立的地址空间(共享) 区别&#xff1a;在于是否共…

html前端的几种加密/解密方式

HTML前端的加密解密方式有以下几种&#xff1a; 一、base64加密 Base64编码&#xff1a;Base64是一种将二进制数据转换为可打印字符的编码方式。在前端&#xff0c;可以使用JavaScript的btoa()函数进行Base64编码&#xff0c;使用atob()函数进行解码。 var str "hello…

uniapp发行H5获取当前页面query

阅读uni的文档大致可得通过 onLoad与 onShow()的形参都能获取页面传递的参数&#xff0c;例如在开发时鼠标移动到方法上可以看到此方法的简短介绍 实际这里说的是打开当前页面的参数&#xff0c;在小程序端的时候测试并无问题&#xff0c;但是发行到H5时首页加载会造成参数获取…

掌握mysql,看完这篇文章就够了

1.mysql 学习数据库就是学习数据库管理系统&#xff08;一个数据管理系统可以管理多个数据库&#xff09; 安装 安装版本&#xff1a;mysql -installter -community-5.7.41.0.msl 流程 1.custom自定义安装 2.选择安装MySQl Server 5.7.41 - x64 3.默…