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 64
  
  
  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;
  }
                                                                             
  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)
          {
              //child
              execvp(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.txt
          if(*end == '>')
          {
              //ls -al >> log.txt
              if((*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)
          {
              //child
              int 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.mfbz.cn/a/443751.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java 哨兵线性搜索

顾名思义&#xff0c;哨兵线性搜索是线性搜索的一种&#xff0c;与传统线性搜索相比&#xff0c;比较次数减少了。在传统的线性搜索中&#xff0c;仅进行N次比较&#xff0c;而在哨兵线性搜索中&#xff0c;哨兵值用于避免任何越界比较&#xff0c;但没有专门针对正在搜索的元素…

springMVC自定义类型转换

目录 &#x1f34b;&#x1f34a;自定义的转换类 &#x1f34b;&#x1f34a;xml文件中添加配置 &#x1f34b;&#x1f34a;测试 SpringMVC 底层已经封装了很多的类型转换器&#xff0c;也就是为什么我们页面上传的字符串可以使用 Integer接收或者可以直接转换为数组的原因…

学习 考证 帆软 FCP-FineBI V6.0 考试经验

学习背景&#xff1a; 自2024年1月起&#xff0c;大部分时间就在家里度过了&#xff0c;想着还是需要充实一下自己&#xff0c;我是一个充满热情的个体。由于之前公司也和帆软结缘&#xff0c;无论是 Fine-Report 和 Fine-BI 都有接触3年之久&#xff0c;但是主要做为管理者并…

高级语言讲义2010计专(仅高级语言部分)

1.编写一程序&#xff0c;对输入的正整数&#xff0c;求他的约数和。 如&#xff1a;18的约数和为1236939 #include <stdio.h>int getsum(int n){int i,sum0;for(i1;i<n;i)if(n%i0)sumi;return sum; } int main(){int sum getsum(18);printf("%d",sum); …

JavaWeb--Mybatis

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

《Effective Modern C++》- 极精简版 15-21条

本文章属于专栏《业界Cpp进阶建议整理》 继续上篇《Effective Modern C》- 极精简版 5-14条。本文列出《Effective Modern C》的15-21条的个人理解的极精简版本。 Item15、尽量使用constexpr constexpr形容对象 constexpr对象都是const&#xff0c;但是const对象不一定是conste…

全网最最最详细centos7如何安装docker教程

在CentOS 7上安装Docker主要包括以下步骤&#xff1a; 1. 卸载旧版本的Docker 首先&#xff0c;需要确保系统上没有安装旧版本的Docker。可以通过以下命令来卸载它们&#xff1a; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-late…

C++篇 语 句

到目前为止&#xff0c;我们只见过两种语句&#xff1a; return 语句和表达式语句。根据语句对执行顺 序的影响&#xff0c;C 语言其余语句大多属于以下 3 大类。 选择语句&#xff1a; if 语句和 switch 语句。循环语句&#xff1a; while 语句&#xff0c; do...while 语句和…

SSH移植到BusyBox

手动编译SSH安装挺麻烦的&#xff0c;本文主要是我大量借鉴和实践总结出来的流程&#xff0c;一步一按照做不会有太大问题。 移植平台&#xff1a;IMX6UL(迅为开发板) 根文件系统&#xff1a;BusyBox 所有操作都建议不要在root账户下运行&#xff0c;并且make install的安装路…

【FFmpeg】ffmpeg 命令行参数 ⑤ ( 使用 ffmpeg 命令提取 音视频 数据 | 保留封装格式 | 保留编码格式 | 重新编码 )

文章目录 一、使用 ffmpeg 命令提取 音视频 数据1、提取音频数据 - 保留封装格式2、提取视频数据 - 保留封装格式3、提取视频数据 - 保留编码格式4、提取视频数据 - 重新编码5、提取音频数据 - 保留编码格式6、提取音频数据 - 重新编码 一、使用 ffmpeg 命令提取 音视频 数据 1…

《2024国家自然科学基金青年基金》 相关申请注意事项解读

一 年龄计算 2004 对应 89 2005 对应 90 2006 对应 91 2007 对应 92 2008 对应 93 2009 对应 94 2010 对应 95 .。。 二 资助比例&#xff08;2023&#xff09; 2024年 23.13% 2023年 24% 三 2024年政策变动&#xff0c;只能申请3年的30万&#xff0c;不能像23年一样选择10-20的…

【二十九】springboot高并发示例

本章演示在springboot项目中的高并发demo&#xff0c;演示导致的问题&#xff0c;以及单机部署下的解决方案和集群部署下的解决方式以及分布式下的解决方案。 目录 一、单机模式下高并发问题 二、集群模式下高并发问题 一、单机模式下高并发问题 前提&#xff1a;先写一个减扣…

TI IWR6843ISK ROS驱动程序搭建

1、设备准备 1.1 硬件设备 1&#xff09;TI IWR 6843 ISK 1块 2&#xff09;Micro USB 数据线 1条 1.2 系统环境 1&#xff09;VMware Workstation 15 Player 虚拟机 2&#xff09;Ubuntu18.04 并安装有 ROS1 系统 如若没有安装 ROS 系统&#xff0c;可通过如下指令进行…

腾讯云轻量服务器流量用完了怎么办?停机吗?

腾讯云轻量服务器流量用完了怎么办&#xff1f;超额流量另外支付流量费&#xff0c;流量价格为0.8元/GB&#xff0c;会自动扣你的腾讯云余额&#xff0c;如果你的腾讯云账号余额不足&#xff0c;那么你的轻量应用服务器会面临停机&#xff0c;停机后外网无法访问&#xff0c;继…

深入了解XSS攻击:原理、防御与应对策略

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

结构体内存对齐详解

目录 结构体对齐&#xff1a; 为什么要进行内存对齐&#xff1f; 关于结构体的详解文章&#xff1a;C语言结构体详解_结构体变量和结构体类型举例-CSDN博客 结构体对齐&#xff1a; 存储的时候和当前存储的成员类型字节大小和默认对齐数比较&#xff0c;取小值 存在该对齐数的…

PodMan容器技术

容器 容器技术 软件应用通常依赖于运行时环境提供的系统库、配置文件或服务。传统上&#xff0c;软件应用的运行时环境安装 在物理主机或虚拟机上运行的操作系统中。 然后&#xff0c;管理员在操作系统上安装应用依赖项。 在RHEL中&#xff0c;诸如 RPM 等打包系统可协助管…

Docker MySQL 报 2059 错误:认证插件 ‘caching_sha2_password‘ 无法加载

使用docker部署的mysql8.0.29再使用Navicat连接myslq报错Authentication plugin ‘xxxxxxx’ cannot be loaded&#xff1a;XXXXXX &#xff08;无法加载身份验证插件&#xff09; 原因&#xff1a;mysql8 之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规…

嘉绩咨询:八位一体产业创新,赋能品牌新零售

探索新零售领域不断创新高峰的嘉绩咨询在今天全面展现了其“八位一体”产业创新模式&#xff0c;该模式旨在为新零售品牌提供全方位的赋能服务。立足于广州的企业战略导航专家&#xff0c;吹响了帮助中国品牌实现全球化发展的号角。 嘉绩咨询的核心业务涵盖招商教育、招商落地、…
最新文章