【Linux】重定向dup

news2024/11/25 2:59:11

文章目录

  • 前言
  • 重定向的原理
  • dup函数
  • 添加重定向功能到myshell


前言

了解重定向之前需要明白文件描述符的工作规则,可以看这篇文章:文件系统

最关键的一点是:在进程中,在文件描述符表中,会将最小的、没有被使用的数组元素分配给新文件。

重定向的原理

输出重定向

以下是测试代码:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LOG "log.txt"

int main()
{
    close(1);//关闭标准输出
    int fd = open(LOG, O_CREAT | O_WRONLY | O_TRUNC, 0666);

    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");

    return 0;
}

如果我们把close(1);//关闭标准输出这句代码先注释掉,运行结果如下——打印到显示屏上。1号文件描述符对应的就是stdout文件。
在这里插入图片描述
那如果我们把close(1);//关闭标准输出这句代码加上,运行结果如下——可以发现,运行后发现没有在命令行打印,并且在生成的log.txt文件中发现了5行hello world!
在这里插入图片描述
解析:
首先因为0,1,2号文件描述符分别对应标准输入输出和错误,我们关闭1号描述符即关闭了stdout;又因为文件描述符的分配规则:在文件描述符表中,会将最小的、没有被使用的数组元素分配给新文件。所以当我们打开新的文件log.txt的时候,会将现在空出来的1号文件描述符分配给log.txt文件;而printf默认向显示器打印,即默认输出到准输出流stdout,但是printf是根据数组下标来打印的,它并不知道对应数组下标的fd实际指向的是谁,此时1号文件描述符指向的是log.txt,所以就会发生打印输出到log.txt文件的现象,实际上着就是输出重定向。

输入重定向

首先我们向log.txt文件中,写入数据:1 2
在这里插入图片描述

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<stdio.h>

#define LOG "log.txt"

int main()
{
    close(0);//关闭标准输入
    int fd = open(LOG, O_RDONLY);

    int a,b;
    scanf("%d %d",&a,&b);
    printf("a=%d b=%d\n",a,b);                                                                                                                                                 
    return 0;
} 

运行结果如下:
在这里插入图片描述
解析:
对于输入重定向,同样的方法,以O_RDONLY(只读模式)打开文件,如果关闭0号文件描述符标准输入stdin,打开文件后会将文件地址填入0号下标,此时在进程中如果使用scanf输入,就不会从默认的标准输入stdin——键盘输入,而是直接从打开的文件中读取数据并输入。

追加重定向

对于追加重定向,将输出重定向中打开文件的方式由O_TRUNC(对文件内容做清空)改为O_APPEND(追加)即可,所以输出重定向与追加重定向唯一的区别就是文件打开的方式不同。

重定向原理:
在上层无法感知的情况下,在OS操作系统内部,更改进程对应的文件描述符表中,特定下标的指向!!!这个特定下标指的就是0——标准输入,1——标准输出,2——标准错误!!!

在这里插入图片描述


再看一个测试,请看下列代码:

  1 #include<iostream>
  2 #include<cstdio>                                                                                                                                                                 
  3 
  4 int main()
  5 {
  6     //Linux下一切皆文件
  7     //C
  8     printf("hello printf->stdout\n");
  9     fprintf(stdout,"hello fprintf->stdout\n");
 10     fprintf(stderr,"hello fprintf->stderr\n");
 11 
 12     //C++
 13     std::cout << "hello cout -> cout" << std::endl;
 14     std::cerr << "hello cerr -> cerr" << std::endl;
 15 }

运行结果:标准输出和标准错误都会向显示器打印。

在这里插入图片描述
而输出重定向后,只有fprintf指定文件流stdout的与cout的打印到了文件log.txt中,而stderr与cerr还依旧向显示器文件打印。重定向时只会对标准的输出进行正常的重定向,但是标准错误不受重定向的影响。
在这里插入图片描述
解析:

  1. stdout,cout——>1,他们都是向1号文件描述符对应的文件进行打印。而输出重定向,只改变1号对应的指向,重定向后1号指向了log.txt文件,所以printf,fprintf,cout就往log.txt文件中打印了。
  2. stderr,cerr——>2,他们都是向2号文件描述符对应的文件进行打印。所以不影响2号。

经过以上学习,那么如何把常规消息,打印到log_normal,异常消息打印到log_error呢?

测试代码:

  1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <stdio.h>
  6 
  7 #define LOG_NORMAL "logNormal.txt"
  8 #define LOG_ERROR "logError.txt"
  9 
 10 int main()
 11 {
 12     printf("hello printf->stdout\n");
 13     printf("hello printf->stdout\n");
 14     printf("hello printf->stdout\n");
 15     printf("hello printf->stdout\n");
 16     printf("hello printf->stdout\n");
 17 
 18     fprintf(stdout,"hello fprintf->stdout\n");
 19     fprintf(stdout,"hello fprintf->stdout\n");
 20     fprintf(stdout,"hello fprintf->stdout\n");
 21     fprintf(stderr,"hello fprintf->stdout\n");
 22     fprintf(stdout,"hello fprintf->stdout\n");
 23     fprintf(stdout,"hello fprintf->stdout\n");
 24 
 25     return 0;                                                                                                                                                                    
 26 }

运行结果:所以打印信息都粘在一块了
在这里插入图片描述
正确写法:

  1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <stdio.h>
  6 
  7 #define LOG_NORMAL "logNormal.txt"
  8 #define LOG_ERROR "logError.txt"
  9 
 10 int main()
 11 {
 12     close(1);//关闭标准输出-stdout
 13     open(LOG_NORMAL, O_WRONLY | O_CREAT | O_APPEND, 0666);//这个时候1号文件描述符对应的是logNormal.txt文件
 14     //所以原来应该输出到stdout显示器的文件,会被输到logNormal.txt文件中
 15 
 16     close(2);//关闭标准错误-stderr 
 17     open(LOG_ERROR, O_WRONLY | O_CREAT | O_APPEND, 0666);                                                                                                                        
 18 
 19     printf("hello printf->stdout\n");
 20     printf("hello printf->stdout\n");
 21     printf("hello printf->stdout\n");
 22     printf("hello printf->stdout\n");
 23     printf("hello printf->stdout\n");
 24 
 25     fprintf(stdout,"hello fprintf->stdout\n");
 26     fprintf(stdout,"hello fprintf->stdout\n");
 27     fprintf(stdout,"hello fprintf->stdout\n");
 28     fprintf(stderr,"hello fprintf->stderr\n");
 29     fprintf(stdout,"hello fprintf->stdout\n");
 30     fprintf(stdout,"hello fprintf->stdout\n");
 31 
 32     return 0;
 33 }

运行结果:
在这里插入图片描述
在这里插入图片描述

也可以在命令行输入以下代码,进行重定向:

./myproc 1>log.txt 2>err.txt

上述代码,我们用close函数,分别将1,2号关闭,log_normal,txt文件对应的文件描述符就变成了1,log_error文件的描述符对应的就是2,而stdout和stderr默认指向1,2号文件描述符,所以以后文件量大的话,就可以使用这种方法将错误消息都筛选到一个文件中,便于观察。

dup函数

前面讲原理时所用的方法:关闭相关的文件描述符指向的文件,然后再打开文件来实现重定向。这种方法平时不会使用,只是讲原理时使用。下面介绍重定向的实际使用方法:利用dup函数进行重定向

dup2如果调用成功,返回newfd,否则返回-1。
在这里插入图片描述

> log.txt //一个大于符号+文件名即可清空这个文件

dup函数的使用方式:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5 #include<sys/types.h>
  6 #include<sys/stat.h>
  7 #include<fcntl.h>
  8 
  9 #define LOG_NORMAL "logNormal.txt"
 10 #define LOG_ERROR "logError.txt"
 11 
 12 int main()
 13 {
 14     int fd = open(LOG_NORMAL, O_CREAT | O_WRONLY | O_APPEND, 0666);
 15     if(fd<0)
 16     {
 17         perror("open error");
 18         return 1;
 19     }
 20 
 21     dup2(fd,1); 1号文件描述符重定向到fd1                                                                                                                                                                
 22 
 23     printf("hello world!\n");
 24 
 25     close(fd);//关闭fd
 26 }

运行结果:
在这里插入图片描述

添加重定向功能到myshell

这里的myshell指的是我们自己实现的简易命令行解释器,想学习之前实现的myshell可以看这篇文章:进程控制——简易shell

思路如下:

  1. 判断获取到的命令,遍历命令字符串,看看是否有>输出重定向,>>追加重定向,<输入重定向。
  2. 设置flag变量,用于标记重定向的类型,flag为0表示命令当中包含输出重定向,flag为1表示命令中包含追加重定向,flag为2表示输入重定向。
  3. 找到重定向之后,用flag标记它是哪种重定向之后,将这个位置置成’\0’,跳过一个空格后,后面跟的就是目标文件名,若flag为0,则以写的方式打开目标文件;若type值为1,则以追加的方式打开目标文件,若flag为2,则以读的方式打开目标文件。
  4. 若flag为0或1,则使用dup2函数实现目标文件与标准输出流的重定向;若flag为2,则使用dup2函数实现目标文件与标准输入流的重定向。

代码实现如下:

#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LEN 1024 //命令最大长度
#define NUM 32 //命令拆分后的最大个数
int main()
{
	int flag = 0; //0 >, 1 >>, 2 <
	char cmd[LEN]; //存储命令
	char* myargv[NUM]; //存储命令拆分后的结果
	char hostname[32]; //主机名
	char pwd[128]; //当前目录
	while (1){
		//获取命令提示信息
		struct passwd* pass = getpwuid(getuid());
		gethostname(hostname, sizeof(hostname)-1);
		getcwd(pwd, sizeof(pwd)-1);
		int len = strlen(pwd);
		char* p = pwd + len - 1;
		while (*p != '/'){
			p--;
		}
		p++;
		//打印命令提示信息
		printf("[%s@%s %s]$ ", pass->pw_name, hostname, p);
		//读取命令
		fgets(cmd, LEN, stdin);
		cmd[strlen(cmd) - 1] = '\0';

		//实现重定向功能
		char* start = cmd;
		while (*start != '\0'){
			if (*start == '>'){
				flag = 0; //遇到一个'>',输出重定向
				*start = '\0';
				start++;
				if (*start == '>'){
					flag = 1; //遇到第二个'>',追加重定向
					start++;
				}
				break;
			}
			if (*start == '<'){
				flag = 2; //遇到'<',输入重定向
				*start = '\0';
				start++;
				break;
			}
			start++;
		}
		if (*start != '\0'){ //start位置不为'\0',说明命令包含重定向内容
			while (isspace(*start)) //跳过重定向符号后面的空格
				start++;
		}
		else{
			start = NULL; //start设置为NULL,标识命令当中不含重定向内容
		}

		//拆分命令
		myargv[0] = strtok(cmd, " ");
		int i = 1;
		while (myargv[i] = strtok(NULL, " ")){
			i++;
		}
		pid_t id = fork(); //创建子进程执行命令
		if (id == 0){
			//child
			if (start != NULL){
				if (flag == 0){ //输出重定向
					int fd = open(start, O_WRONLY | O_CREAT | O_TRUNC, 0664); //以写的方式打开文件(清空原文件内容)
					if (fd < 0){
						error("open");
						exit(2);
					}
					close(1);
					dup2(fd, 1); //重定向
				}
				else if (flag == 1){ //追加重定向
					int fd = open(start, O_WRONLY | O_APPEND | O_CREAT, 0664); //以追加的方式打开文件
					if (fd < 0){
						perror("open");
						exit(2);
					}
					close(1);
					dup2(fd, 1); //重定向
				}
				else{ //输入重定向
					int fd = open(start, O_RDONLY); //以读的方式打开文件
					if (fd < 0){
						perror("open");
						exit(2);
					}
					close(0);
					dup2(fd, 0); //重定向
				}
			}

			execvp(myargv[0], myargv); //child进行程序替换
			exit(1); //替换失败的退出码设置为1
		}
		//shell
		int status = 0;
		pid_t ret = waitpid(id, &status, 0); //shell等待child退出
		if (ret > 0){
			printf("exit code:%d\n", WEXITSTATUS(status)); //打印child的退出码
		}
	}
	return 0;
}

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

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

相关文章

vscode整合gitee

vscode需要下载的插件 第一个可以多仓库进行操作 第二个主要是用于仓库的管理和展示 vscode的gitee操作 1、按F1&#xff0c;搜索gitee 2、根据提示进行操作 标1的是第一个插件的操作 标2的是第二个插件的操作 绑定用户私钥 两个插件绑定私钥的方式不同&#xff0c; gitee的私…

这本数智平台白皮书讲透了大型企业数智化升级业务痛点

在以“升级企业数智化底座”为主题的2023用友BIP技术大会上&#xff0c;用友联合全球权威咨询机构IDC共同发布《建设数字中国 升级数智底座——企业数智化底座白皮书》&#xff0c;在这本数智平台白皮书里深入剖析了大型企业的数智化升级痛点。 大型企业普遍具有广域的业务覆盖…

六级备考15天|CET-6|翻译真题练习|北京大兴国际机场|9:15~10:20

目录 中文 英文 词汇 订正 解析 练习 中文 英文 词汇 put sth. into use 投入使用 距离south of地点 “...以南....公里处” construction 开工建设 the giant project 巨型工程 on the site …

LED显示屏驱动IC基本原理

LED显示屏驱动IC&#xff08;Integrated Circuit&#xff0c;集成电路&#xff09;是一种专门设计用于控制和驱动LED显示屏的电子元件。LED显示屏驱动IC的基本原理涉及到LED的电流控制、亮度调节、扫描控制和图像数据处理等方面。 以下是LED显示屏驱动IC的基本原理的详细说明&a…

只需简单几步,就能在报表工具FastReport .NET 中使用 RFID 标签

FastReport 是功能齐全的报表控件&#xff0c;可以帮助开发者可以快速并高效地为.NET&#xff0c;VCL&#xff0c;COM&#xff0c;ActiveX应用程序添加报表支持&#xff0c;由于其独特的编程原则&#xff0c;现在已经成为了Delphi平台最优秀的报表控件&#xff0c;支持将编程开…

喜讯丨计讯物联5G物联网数据网关TG463荣登2022年度中国物联网行业创新产品榜

近日&#xff0c;备受瞩目的2022‘物联之星’中国物联网产业年度榜单颁奖典礼在上海世博展览馆会场隆重举行。经由申报筛选、网络人气投票、专家评委投票等多重环节&#xff0c;计讯物联旗下5G物联网数据网关TG463荣登2022年度中国物联网行业创新产品榜。 作为中国物联网行业…

chatgpt赋能python:Python编写抽奖程序——让你的活动更加有趣

Python编写抽奖程序——让你的活动更加有趣 在现代社会中&#xff0c;抽奖活动已经成为了许多商家和组织吸引关注、增强互动的重要手段。而使用Python编写抽奖程序可以帮助我们更加方便地进行这一活动。本文将介绍Python编写抽奖程序的方法&#xff0c;以及如何在实际应用中优…

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等)、趣味项目实现、学术应用项目实现

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 对于深度强化学习这块规划为&#xff1a; 基础单智能算法教学&#xff08;gym环境为主&#xff09;主流多智能算法教学…

Hive和Hadoop关系

Hive是基于Hadoop的一个数据仓库工具&#xff0c;用来进行数据提取、转化、加载&#xff0c;这是一种可以查询和分析存储在Hadoop中的大规模数据的机制。Hive数据仓库工具能将结构化的数据文件映射为一张数据库表&#xff0c;并提供SQL查询功能&#xff0c;能将SQL语句转变成Ma…

vue组件库 vue marquee vue跑马灯 vue走马灯 ​vue-marquee-text-component​中文

vue2运行安装 npm install vue-marquee-text-component1.2.0 Name说明DescriptionConfigduration持续时间动画持续时间&#xff5b;类型&#xff1a;数字&#xff0c;默认值&#xff1a;15&#xff5d;Animation Duration{ type: Number, default: 15 }repeatrepeat重复插槽的…

chatgpt赋能python:Python内置函数:如何查找和使用?

Python内置函数&#xff1a;如何查找和使用&#xff1f; 作为一名有10年Python编程经验的工程师&#xff0c;我想与大家分享一下Python内置函数的使用技巧。Python内置函数是指已经定义好的函数&#xff0c;无需另外安装也无需导入就可以直接在Python中使用的函数。这篇文章将…

chatgpt赋能python:Python编程教程:如何用Python写抢购程序

Python编程教程&#xff1a;如何用Python写抢购程序 随着网购的流行和限量商品的推出&#xff0c;抢购已经成为了一个非常热门的话题。有些人甚至会通过软件或程序来提高他们成功抢到商品的机会。在本篇文章中&#xff0c;我们将介绍如何用Python编写一个简单易用的抢购程序&a…

客户频繁变更需求,项目经理该如何应对?

王博刚当上项目经理&#xff0c;接手了一个中型软件项目。公司高层多次提醒他要尊重客户需求&#xff0c;并充分满足客户的期望。 一开始项目进展顺利&#xff0c;但后来客户频繁变更需求给团队带来了很多额外工作。王博动员大家加班保证项目进度&#xff0c;让客户非常满意。…

【面上对象三大特性之多态】

目录 1.什么是多态2.多态的条件 3.向上转型3.1向上转型的三种写法3.2 优缺点 4.向下转型&#xff08;用得少&#xff09;5.避免在构造方法中调用重写的方法6.重写6.1什么是重写6.2 重写和重载的区别 1.什么是多态 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&…

PARSeq论文翻译

《Scene Text Recognition with Permuted Autoregressive Sequence Models》论文翻译 文章目录 《Scene Text Recognition with Permuted Autoregressive Sequence Models》论文翻译Abstract1、Introduction2、Related Work3、Permuted Autoregressive Sequence Models3.1、Mod…

MySQL数据库优化看这一篇就够了(最全干货篇)

文章目录 一、MySQL 主备切换以及读写分离二、SQL优化1、如何定位低效率的SQL语句-慢查询日志&#xff1f;EXPLAIN 执行计划怎么分析&#xff1f;2、sql语句优化常用的方法有哪些&#xff1f;3、如何优化索引&#xff1f;优化CRUD操作&#xff1f;优化分页&#xff1f;4、通过s…

C语言进阶之数据的存储

数据的存储 1. 数据类型介绍1.1 类型的基本归类 2.整型在内存中的存储2.1原码、反码、补码大小端介绍2.3练习 3. 浮点型在内存中的存储3.1浮点数存储规则 1. 数据类型介绍 在C语言初阶之数据类型这篇博客中&#xff0c;我们提到了关于数据内存的大小和基本用法 链接&#xff1…

大模型浪潮下的平台、框架、AI编译器和芯片架构 | 智源大会AI系统论坛

6月9日&#xff0c;2023北京智源大会&#xff0c;将邀请这一领域的探索者、实践者、以及关心智能科学的每个人&#xff0c;共同拉开未来舞台的帷幕&#xff0c;与会知名嘉宾包括&#xff0c;2019年图灵奖得主Yann LeCun、图灵奖得主Geoffrey Hinton、图灵奖得主Joseph Sifakis、…

童心不泯,因AI飞驰,祝大朋友小朋友六一儿童节快乐!

AIGC 大模型&#xff0c;让所想&#xff0c;即所见。 在青少年想象力培养上&#xff0c; 生成式人工智能技术的蓬勃发展&#xff0c; 让人类想象力和创造力提升&#xff0c; 迎来了全新的发展阶段。 百度通过大模型与深度学习框架融合发展&#xff0c;打造了自主创新的AI底座&a…

JavaWeb16(sessionCookie)

目录 1.会话跟踪&HTTP无状态协议. 1.1会话跟踪. 1.2HTTP无状态协议. 2.Cookie. 2.1何为Cookie. 2.2Cookie的有效期. 2.3案例讲解. 2.3.1用cookie做登陆状态验证. 2.3.2验证cookie是将数据保存到客户端及有效期. 2.3.2用cookie做注销. 2.3.3用cookie实现自动登陆…