Linux——文件重定向

news2025/6/18 1:39:52

目录

前言

一、重定向 

二、重定向的运用

三、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.coloradmin.cn/o/1503722.html

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

相关文章

基于springboot实现图书推荐系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现图书馆推荐系统演示 摘要 时代的变化速度实在超出人类的所料&#xff0c;21世纪&#xff0c;计算机已经发展到各行各业&#xff0c;各个地区&#xff0c;它的载体媒介-计算机&#xff0c;大众称之为的电脑&#xff0c;是一种特高速的科学仪器&#xff0c;比…

软件测试自学和报班学习的区别,各有各的优势和缺点,大家看完之后自己选择喔

时代在进步&#xff0c;人们汲取知识的方式不再是单一的在书本上面&#xff0c;现在网络发达&#xff0c;只需要上网就能找到相关的好多知识&#xff0c;慢慢的大家越来越觉得有了这些知识&#xff0c;只要自己有自制力就完全能够自学到一定的程度。 在自学氛围的影响下&#…

深度学习+感知机

深度学习感知机 1感知机总结 2多层感知机1XOR2激活函数3多类分类总结 3代码实现 1感知机 是个很简单的模型,是个二分类的问题。 感知机&#xff08;perceptron&#xff09;是Frank Rosenblatt在1957年提出的一种人工神经网络&#xff0c;被视为一种最简单形式的前馈神经网络&…

could not publish server configuration for tomcat at localhost

1&#xff0c;报错信息如图&#xff1a; 2&#xff0c;找到servers双击&#xff0c;选择Modules&#xff0c;如果有两个webModules ,remove一个&#xff0c; 3&#xff0c;如果重启还是报错&#xff0c;干脆两个都remove&#xff0c;双击tomcat服务add And Remove重新添加

【算法沉淀】最长回文子串

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

mxxWechatBot流程与原理

大家伙&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 免责声明&#xff1a;该工具仅供学习使用&#xff0c;禁止使用该工具从事违法活动&#xff0c;否则永久拉黑封禁账号&#xff01;&#xff01;&#xff01;本人不对任何工具的使用负责&am…

【NR 定位】3GPP NR Positioning 5G定位标准解读(九)-增强的小区ID定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

【学习笔记】Vue3源码解析:第二部分-实现响应式(5)

课程地址&#xff1a;【已完结】全网最详细Vue3源码解析&#xff01;&#xff08;一行行带你手写Vue3源码&#xff09; 第二部分-实现响应式&#xff08;5&#xff09;&#xff1a;&#xff08;对应课程的第18-21节&#xff09; 第18节&#xff1a;《创建ref实例》 vue3中 re…

机器学习--循环神经网络(RNN)1

一、简介 循环神经网络&#xff08;Recurrent Neural Network&#xff09;是深度学习领域中一种非常经典的网络结构&#xff0c;在现实生活中有着广泛的应用。以槽填充&#xff08;slot filling&#xff09;为例&#xff0c;如下图所示&#xff0c;假设订票系统听到用户说&…

【数据分享】2013-2022年全国范围逐月CO栅格数据(免费获取)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000-2022年全国范围逐月的PM2.5栅格数据和2013-2022年全国范围逐月SO2栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;。 本次我们给大家带来的是2013-2022年全国范围的逐月的CO栅格…

ubuntu22.01安装及配置

前言 本次安装基于VMware Pro 16进行安装。 ubuntu版本&#xff1a;ubuntu-22.04.3-live-server-amd64.iso 1、下载 1.1官网下载 https://ubuntu.com/download 1.2、清华大学镜像网站下载 https://mirrors.tuna.tsinghua.edu.cn/ 进入网站后搜索ubuntu&#xff0c;选择ubu…

基于PSO粒子群算法的三角形采集堆轨道优化matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 假设一个收集轨道&#xff0c;上面有5个采集堆&#xff0c;这5个采集堆分别被看作一个4*20的矩阵&#xff08;下面只有4*10&#xff09;&#xff0c;每个模块&…

RabbitMQ发布确认高级版

1.前言 在生产环境中由于一些不明原因&#xff0c;导致 RabbitMQ 重启&#xff0c;在 RabbitMQ 重启期间生产者消息投递失败&#xff0c; 导致消息丢失&#xff0c;需要手动处理和恢复。于是&#xff0c;我们开始思考&#xff0c;如何才能进行 RabbitMQ 的消息可靠投递呢&…

Java学习笔记NO.18

T1.理工超市 &#xff08;1&#xff09;题目描述 编写一个程序&#xff0c;设计理工超市功能菜单并完成注册和登录功能的实现。显示完菜单后&#xff0c;提示用户输入菜单项序号。当用户输入<注册>和<登录>菜单序号时模拟完成注册和登录功能&#xff0c;最后提示…

VMware安装CentOS7操作系统超详细图文教程

文章目录 一.下载镜像二.镜像安装三.继续进行虚拟机设置四.虚拟机启动设置一些供参考的视频教程 一.下载镜像 官网镜像下载地址(需要开梯) 清华大学镜像站地址 二.镜像安装 三.继续进行虚拟机设置 四.虚拟机启动设置 点击虚拟机后鼠标就不会出VMare,想要鼠标返回个人的计算机…

Java多线程学习的关键要点和常见案例总结

文章目录 Java多线程学习的关键要点&#xff1a;案例示例&#xff1a; Java多线程编程还包括更多的高级特性和实用技巧高级主题&#xff1a;实用案例&#xff1a;线程池的高级用法和配置&#xff1a;线程安全的最佳实践&#xff1a; Java多线程学习的关键要点和常见案例总结如下…

数字孪生10个技术栈:数据传输的四个问题

大家好&#xff0c;我是贝格前端工场&#xff0c;上期讲了数据采集的八个方式&#xff0c;数据采集之后就要进行数据的处理&#xff0c;本期继续分享&#xff0c;大家如有数字孪生或者数据可视化的需求&#xff0c;可以联络我们。 一、什么是数据处理 在数字孪生中&#xff0c…

如何配置IDEA中的JavaWeb环境(2023最新版)

创建项目 中文版&#xff1a;【文件】-【新建】-【项目】 点击【新建项目】&#xff0c;改好【名称】点击【创建】 右键自己建立的项目-【添加框架支持】&#xff08;英文版是Add Framework Support...&#xff09; 勾选【Web应用程序】-【确定】 配置tomcat 点击编辑配置 点…

【C++ 学习】拷贝构造你了解多少?

文章目录 1. 拷贝构造的引入2. 拷贝构造的引用场景 1. 拷贝构造的引入 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存在的类类型对象创建新对象时由编译器自动调用&#xff1b; 特征&#xff1a; ① …

算法第二十五天-寻找排序数组中的最小值

寻找排序数组中的最小值 题目要求 解题思路 二分法 代码 class Solution:def findMin(self, nums: List[int]) -> int:low, high 0, len(nums) - 1while low < high:pivot low (high - low) // 2if nums[pivot] < nums[high]:high pivot else:low pivot 1re…