当前位置: 代码网 > 服务器>服务器>Linux > 【Linux】重定向 | 为什么说”一切皆文件?“

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

2024年08月06日 Linux 我要评论
讲解了Linux中文件描述符的管理、dup2接口与重定向技术,涵盖输出、追加及输入重定向,最后对“一切皆文件”理念进行理解。

目录

前言

1.文件描述符分配规则

2.dup2 重定向接口

3.重定向

3.1>输出重定向

3.2>>追加重定向

3.3<输入重定向

3.4 shell 模拟实现< >

3.5 理解>

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


前言

💬 前面还存在默认标准文件,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;
}

运行;

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

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


2.dup2 重定向接口

man dup2

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

具体应用: 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;
}

运行:

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 读取。

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) {//遍历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++;
    }
}
  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 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;
}
  1. 占位符函数
    • 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 //实现了覆盖

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

❗ 对于重定向的记忆

cat(cout)<<输出

>>(cin)输入到

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


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

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

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

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

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


思考:

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

2. 万变不离其宗:

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

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com