首页云计算【Linux】重定向 | 为什么说”一切皆文件?“

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

时间2024-07-15 00:00:25发布ongwu分类云计算浏览86

目录

前言

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[]);
check_redir: 该函数处理命令字符串以识别和处理输入(<)、输出(>)和追加(>>)重定向。它更新命令字符串以排除重定向部分,并相应地设置全局变量 rdirfilenamerdir
// 检查并处理命令中的重定向
void check_redir(char *cmd) {
char *pos = cmd;
while (*pos) {//遍历pos
if (*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.txt
pos++;
while (isspace(*pos)) pos++;
rdirfilename = pos;
rdir = IN_RDIR;
break;
}
pos++;
}
}
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);//处理重定向符号
}
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);
}
}
}
main程序的主循环初始化重定向设置,与用户交互以获取命令,将命令行输入分割成参数,并调用 NormalExcute 执行命令。
int main() {
while (!quit) {
rdirfilename = NULL;
rdir = NONE;
interact(commandline, sizeof(commandline));
// Tokenize the command line input into arguments
char *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;
}
占位符函数getpwdgetusernamegethostname1 是用于获取当前工作目录用户名和主机名的占位符函数,应根据需要实现实际功能
void getpwd() {
// Implement function to get current working directory
}
char* getusername() {
// Implement function to get the username
return "user";
}
char* gethostname1() {
// Implement function to get the hostname
return "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. 万变不离其宗:

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

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

Ongwu博客 版权声明:以上内容未经允许不得转载!授权事宜或对内容有异议或投诉,请联系站长,将尽快回复您,谢谢合作!

展开全文READ MORE
Mac电脑硬盘分区中APFS与HFS+我们应该选择哪种格式? 两个Python脚本轻松解决ETL工作:统计多个服务器下所有数据表信息(2)

游客 回复需填写必要信息