【Linux】重定向 | 为什么说”一切皆文件?“

目录

前言

1.文件描述符分配规则

2.dup2 重定向接口

3.重定向

3.1>输出重定向

3.2>>追加重定向

3.3<输入重定向

3.4 shell 模拟实现< >

3.5 理解>

4. 理解“Linux 下一切皆文件”


前言

问:fd 为什么默认从 3 开始,而不是从 0,1,2?

💬 前面还存在默认标准文件,stdin, stdout, stderr ,和 0,1,2一一对应

验证:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(void)
{int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 1;} printf("fd: %d\n", fd);close(fd);
}


1.文件描述符分配规则

从 0 下标开始,寻找最小的没有使用的数组位置,它的下标就是新文件的文件描述符

(1) 关闭0,对返回的 fd 进行测试

 int main(void)
{close(0);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 1;} printf("fd: %d\n", fd);close(fd);
}

运行:

(2)关闭 1

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>int main()
{
// close(0);
// close(2);
close(1);
umask(0);
int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
if(fd == -1)
{
perror("open");
exit(1);
}
printf("fd:%d\n",fd);//这里必须刷新一下,不然log.txt里面没有内容,这里和缓冲区有关,下面讲                                                                                                                  
fflush(stdout);      
close(fd);           return 0;
}

运行;

❓ 本来应该直接打到显示器上的消息,打印到了 log.txt 上,是为什么呢?

A: 进程不 care 1 文件发生的变化,先关闭再 open,根据验证规则,会实现对 1 文件的补充重定向

❗ 重定向的本质:上层用的 fd 不变,在内核中更改 fd 对应的 struct file* 地址


2.dup2 重定向接口

man dup2

NAME 中提到:dup 复制文件描述符

函数功能:

  • dup2函数将文件描述符oldfd复制到newfd,并返回一个新的文件描述符。如果newfd已经打开,则将先关闭它。新的文件描述符将继承oldfd的文件状态标志(例如,文件偏移量、文件访问模式等)。

函数返回值:

  • 成功时,返回新的文件描述符;失败时,返回-1,并设置errno变量来指示错误类型。

具体应用: new be old ,新的被老的覆盖,所以保留的是 old,dup2(old, new),所以前一个文件会拷贝形成 double 份,如果想实现对之前操作的实现,即 dup2(fd,1)

tip: 拷贝的是下标指针 struct 的 拷贝替换

#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>int main()
{// close(0);// close(2);// close(1);umask(0);int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);if(fd == -1){perror("open");exit(1);}//重定向dup2(fd,1);printf("fd:%d\n",fd);//这里必须刷新一下,不然log.txt里面没有内容fflush(stdout);close(fd);return 0;
}

运行:

fd 覆盖了 log,应该输出到 1 中的,就到 fd 文件里面了

3.重定向

3.1>输出重定向

上面内容就是输出重定向,把新打开文件的fd重定向到fd为1(默认为显示器)的位置。

3.2>>追加重定向

实现在文件原内容后面的添加

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>int main()
{// close(0);// close(2);// close(1);umask(0);// int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);if(fd == -1){perror("open");exit(1);}//重定向dup2(fd,1);printf("add hello\n");//这里必须刷新一下,不然log.txt里面没有内容                                                                           fflush(stdout);                                                                                                       close(fd);                                                                                                            return 0;                                                                                                             
}

3.3<输入重定向

测试:

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>int main()
{// close(0);// close(2);// close(1);umask(0);// int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);// int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);int fd=open("log.txt",O_RDONLY);if(fd == -1){perror("open");exit(1);}//输入重定向dup2(fd,0);char outbuffer[64];while(1){printf("<");if(fgets(outbuffer,sizeof(outbuffer),stdin) == NULL) break;printf("%s",outbuffer);}return 0;
}

输入重定向:

  • 使用 dup2(fd, 0) 将文件描述符 fd 重定向到标准输入(文件描述符0),即从此程序的标准输入将从 log.txt 读取。

读取并输出内容:

  • 声明一个字符数组 outbuffer[64] 用于存储从标准输入读取的内容。
  • 使用 while 循环不断读取标准输入(现在是 log.txt 的内容)直到文件结束。
  • printf("<") 打印一个提示符。
  • fgets(outbuffer, sizeof(outbuffer), stdin) 从标准输入读取一行,如果读取失败(例如文件结束),则退出循环。
  • printf("%s", outbuffer) 将读取的内容打印到标准输出。

fgets(stdin)的显示,由键盘变为了从文件中读取,后并打印

C++联动:

>输入(将内容添加到文件之中),<读取(由从键盘读取变为了从某文件读取 fgets 0->fgets file)


3.4 shell 模拟实现< >

思路:

头文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <assert.h>
#include <ctype.h>// 定义重定向类型
#define NONE 0
#define IN_RDIR 1
#define OUT_RDIR 2
#define APPEND_RDIR 3#define LEFT "<"
#define RIGHT ">"
#define LABLE "$"// 全局变量用于重定向
char *rdirfilename;
int rdir = NONE;
int lastcode;
int quit = 0;
char commandline[1024];// 函数声明
void getpwd();
char* getusername();
char* gethostname1();
void check_redir(char *cmd);
void interact(char *cline, int size);
void NormalExcute(char *_argv[]);
  1. check_redir
  • 该函数处理命令字符串以识别和处理输入(<)、输出(>)和追加(>>)重定向。它更新命令字符串以排除重定向部分,并相应地设置全局变量 rdirfilenamerdir
// 检查并处理命令中的重定向
void check_redir(char *cmd) {char *pos = cmd;while (*pos) {//遍历posif (*pos == '>') {//查找重定向符号,对之后实现替换if (*(pos + 1) == '>') {//追加重定向*pos++ = '\0';*pos++ = '\0';//切换字符并跳过while (isspace(*pos)) pos++;//后面的判断怎么理解呢rdirfilename = pos;//将文件名赋给rdir = APPEND_RDIR;//命令行操作break;} else {*pos = '\0';pos++;while (isspace(*pos)) pos++;rdirfilename = pos;rdir = OUT_RDIR;break;}} else if (*pos == '<') {*pos = '\0'; // ls -a -l -n < filename.txtpos++;while (isspace(*pos)) pos++;rdirfilename = pos;rdir = IN_RDIR;break;}pos++;}
}
  1. interact
    • 该函数提示用户输入,获取命令行,并调用 check_redir 处理任何重定向。
void interact(char *cline, int size) {getpwd();//获取当前工作目录并打印提示符printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname1(), pwd);char *s = fgets(cline, size, stdin);//获取命令行输入assert(s);(void)s;cline[strlen(cline) - 1] = '\0';//移除换行符check_redir(cline);//处理重定向符号
}
  1. NormalExcute
    • 该函数创建一个新进程来执行给定的命令。如果设置了任何重定向,它会在执行命令之前相应地调整文件描述符。
// Function to execute commands
void NormalExcute(char *_argv[]) {pid_t id = fork();if (id < 0) {perror("fork");return;} else if (id == 0) {int fd = 0;if (rdir == IN_RDIR) {//对标记的rdir进行操作判断fd = open(rdirfilename, O_RDONLY);dup2(fd, 0);} else if (rdir == OUT_RDIR) {fd = open(rdirfilename, O_CREAT | O_WRONLY | O_TRUNC, 0666);dup2(fd, 1);} else if (rdir == APPEND_RDIR) {fd = open(rdirfilename, O_CREAT | O_WRONLY | O_APPEND, 0666);//打开操作的mode设置dup2(fd, 1);//对文件进行重定向}execvp(_argv[0], _argv);exit(EXIT_FAILURE);} else {int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid == id) {lastcode = WEXITSTATUS(status);}}
}
  1. main
    • 程序的主循环初始化重定向设置,与用户交互以获取命令,将命令行输入分割成参数,并调用 NormalExcute 执行命令。
int main() {while (!quit) {rdirfilename = NULL;rdir = NONE;interact(commandline, sizeof(commandline));// Tokenize the command line input into argumentschar *argv[64];int argc = 0;char *token = strtok(commandline, " ");while (token != NULL) {argv[argc++] = token;token = strtok(NULL, " ");}argv[argc] = NULL;if (argc > 0) {NormalExcute(argv);}}return 0;
}
  1. 占位符函数
    • getpwdgetusernamegethostname1 是用于获取当前工作目录、用户名和主机名的占位符函数,应根据需要实现实际功能。
void getpwd() {// Implement function to get current working directory
}char* getusername() {// Implement function to get the usernamereturn "user";
}char* gethostname1() {// Implement function to get the hostnamereturn "hostname";
}

运行:

思考:

问:后面我们做了重定向的工作,后面我们在进行程序替换的时候,难道不影响吗???

不影响

❗ 程序替换,和文件访问是并列的关系,mm_struct && file_struct ,程序替换,只会替换代码和数据,不影响曾经打开的重定向文件

联系:可以通过对文件调动,为进程替换提供良好的环境

问:子进程重定向会影响父进程吗?

不会

❗ 子进程会对父进程进行写实拷贝

3.5 理解>

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>#define filename "log.txt"int main()
{fprintf(stdout, "hello normal message\n");fprintf(stdout, "hello normal message\n");fprintf(stdout, "hello normal message\n");fprintf(stdout, "hello normal message\n");fprintf(stdout, "hello normal message\n");fprintf(stderr, "hello error message\n");fprintf(stderr, "hello error message\n");fprintf(stderr, "hello error message\n");fprintf(stderr, "hello error message\n");fprintf(stderr, "hello error message\n");

测试:

./mytest 1>nomal.log 2>err.log
//合并
./mytest 1>all.log 2>&1 //实现了覆盖

>默认指向的是 1,那 2 的设置有什么意义呢?

意义: 将错误信息和正确信息分开放了,便于程序员的查看

❗ 对于重定向的记忆

cat(cout)<<输出

>>(cin)输入到

和原理毫不相关,就是用着发现这样挺好记忆的


4. 理解“Linux 下一切皆文件”

🔔 进程通过对文件的管理实现了对设备的管理设备的信息其实也是以文件的形式存储了

这里可能有这样一个问题,如果同一个文件被多个指针指向,但是某个进程把这个文件关了,会不会影响其他进程对这个文件的操作呢?

A: 其实并不会,一个被打开的文件有引用计数

表明有几个指针指向这个文件,这样做是因为一个文件可能被多个指针指向。如果某个进程关闭文件,影响到其他进程,就不能保证进程的独立性。close关闭文件,其实并不是真正关闭文件,只是把引用计数减1,当只有一个指针指向这个文件,close关闭文件才是真正关闭这个文件

文件有*write file 指针,屏蔽掉了设备的差异化,通过指针指向//C++联想:多态,奇类,派生类


思考:

1. 面向对象,是历史的必然,是先进的表现

2. 万变不离其宗:

学习了底层知识,就可以是我们面对各种语言进行文件读取时,读读文档,就能上手操作了

学习思考:通过不断的实验和提问,来进行学习

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

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

相关文章

人工智能算法工程师(中级)课程4-sklearn机器学习之回归问题与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程4-sklearn机器学习之回归问题与代码详解。回归分析是统计学和机器学习中的一种重要方法&#xff0c;用于研究因变量和自变量之间的关系。在机器学习中&#xff0c;回归算法被广泛应用于…

什么? CSS 将支持 if() 函数了?

CSS Working Group 简称 CSSWG, 在近期的会议中决定将 if() 添加到 CSS Values Module Level 5 中。 详情可见&#xff1a;css-meeting-bot 、[css-values] if() function 当我看到这个消息的时候&#xff0c;心中直呼这很逆天了&#xff0c;我们知道像 less 这些 css 这些预…

前端Vue组件化实践:打造自定义等宽tabs标签组件

在前端开发的世界里&#xff0c;随着业务复杂度的提升和需求的多样化&#xff0c;传统的整体式开发方式已经难以满足快速迭代和高效维护的需求。组件化开发作为一种重要的解决方案&#xff0c;正逐渐受到广大开发者的青睐。本文将结合Vue框架&#xff0c;探讨如何通过组件化开发…

【调试笔记-20240713-Windows-Tauri 多个HTML页面支持】

调试笔记-系列文章目录 调试笔记-20240713-Windows-Tauri 多个HTML页面支持 文章目录 调试笔记-系列文章目录调试笔记-20240713-Windows-Tauri 多个HTML页面支持 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调试环境调试目标 二、调试步骤搜索相似问题 三、应用场…

BUCK电源芯片,电气参数,极限参数,工作特性,引脚功能

概述 在应用DC-DC开关电源芯片时&#xff0c;通常需要关注以下参数&#xff0c;同步与非同步&#xff0c;输入电压&#xff0c;输入电流&#xff0c;输出电压&#xff0c;输出电流&#xff0c;输入输出电容的选择&#xff1b;mosfet选型&#xff0c;电感选型&#xff0c;功耗&a…

数据结构(初阶2.顺序表)

文章目录 一、线性表 二、顺序表 2.1 概念和结构 2.2 分类 2.2.1 静态顺序表 2.2.2 动态顺序表 2.3动态顺序表的实现 1.SeqList.h 2.SeqList.c 打印顺序表 初始化 销毁 增容 尾插 头插 在指定位置之前插入数据 尾删 头删 在指定位置删除数据 3.test.c 一、线性表 线性表&#…

git安装使用gitlab

第一步&#xff1a;下载git 第二步&#xff1a;安装 第三步&#xff1a;配置sshkey 第四步&#xff1a;处理两台电脑的sshkey问题 第一步下载git 网址&#xff1a;Git点Downloads根据你的操作系统选择对应的版本&#xff0c;我的是Windows&#xff0c;所以我选择了Windows …

Java的高级特性

类的继承 继承是从已有的类中派生出新的类&#xff0c;新的类能拥有已有类的属性和行为&#xff0c;并且可以拓展新的属性和行为 public class 子类 extends 父类{子类类体 } 优点 代码的复用 提高编码效率 易于维护 使类与类产生关联&#xff0c;是多态的前提 缺点 类缺乏独…

计算机图形学入门28:相机、透镜和光场

1.前言 相机(Cameras)、透镜(Lenses)和光场(Light Fields)都是图形学中重要的组成部分。在之前的学习中&#xff0c;都是默认它们的存在&#xff0c;所以现在也需要单独拿出来学习下。 2.成像方法 计算机图形学有两种成像方法&#xff0c;即合成(Synthesis)和捕捉(Capture)。前…

JVM:类加载器

文章目录 一、什么是类加载器二、类加载器的应用场景三、类加载器的分类1、分类2、启动类加载器3、Java中的默认类加载器&#xff08;1&#xff09;扩展类加载器&#xff08;2&#xff09;应用程序类加载器&#xff08;3&#xff09;arthas中类加载器相关的功能 四、双亲委派机…

78. UE5 RPG 创建技能数据并初始化技能ui

在上一篇文章里&#xff0c;我们创建了技能的UI&#xff0c;接下来&#xff0c;我们要考虑如何实现对技能UI的填充&#xff0c;肯定不能直接写死&#xff0c;需要有一些方法去实现技能的更新。我们期望能够创建一个技能数据&#xff0c;然后根据数据通过回调的方式实现数据的更…

【经典面试题】是否形成有环链表

1.环形链表oj 2. oj解法 利用快慢指针&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode; bool hasCycle(struct ListNode *head) {ListNode* slow head, *fast…

UNIAPP_ReferenceError: TextEncoder is not defined 解决

错误信息 1、安装text-decoding npm install text-decoding2、main.js import { TextEncoder, TextDecoder } from text-decoding global.TextEncoder TextEncoder global.TextDecoder TextDecoder

【网络安全】Oracle:SSRF获取元数据

未经许可&#xff0c;不得转载。 文章目录 前言正文漏洞利用 前言 Acme 是一家广受欢迎的播客托管公司&#xff0c;拥有庞大的客户群体。与许多大型运营公司一样&#xff0c;Acme 采用了Apiary的服务&#xff0c;使用户能够安全高效地管理他们的播客。 Apiary 于2017年初被Or…

Java SpringBoot 若依 后端实现评论“盖楼“,“楼中楼“功能 递归查询递归组装评论结构

效果图 数据库设计 还可以使用路径模块 一级评论id,二级评论id, 用like最左匹配原则查询子评论 因为接手遗留代码&#xff0c;需要添加字段&#xff0c;改动数据库&#xff0c;我就不改动了&#xff0c;导致我下面递归查询子评论不是很好。 业务代码 Overridepublic List<S…

OpenCV漫水填充函数floodFill函数的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 功能描述 ffloodFill函数是OpenCV库中用于图像处理的一个功能&#xff0c;它用于填充与种子点颜色相近的连通区域。这个函数在很多场景下都非常有用&#x…

电子电气架构 --- 关于DoIP的一些闲思 上

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

上传图片,base64改为文件流,并转给后端

需求&#xff1a; html代码&#xff1a; <el-dialog v-model"dialogPicVisible" title"新增图片" width"500"><el-form :model"picForm"><el-form-item label"图片名称&#xff1a;" :label-width"10…

【数组、特殊矩阵的压缩存储】

目录 一、数组1.1、一维数组1.1.1 、一维数组的定义方式1.1.2、一维数组的数组名 1.2、二维数组1.2.1、二维数组的定义方式1.2.2、二维数组的数组名 二、对称矩阵的压缩存储三、三角矩阵的压缩存储四、三对角矩阵的压缩存储五、稀疏矩阵的压缩存储 一、数组 概述&#xff1a;数…

基于Vue和UCharts的前端组件化开发:实现高效、可维护的词云图与进度条组件

基于Vue和UCharts的前端组件化开发&#xff1a;实现高效、可维护的词云图与进度条组件 摘要 随着前端技术的迅速发展和业务场景的日益复杂&#xff0c;传统的整块应用开发方式已无法满足现代开发的需求。组件化开发作为一种有效的解决方案&#xff0c;能够将系统拆分为独立、…