漫话linux:重定向

1.文件编码:在执行部分文件操作时,系统会返回一个文件描述符方便你快速的找到文件并对文件进行其他操作,而在尝试后会发现文件描述符默认从3开始,那0,1,2代表的是什么呢?0代表标准输入流stdin,1代表标准输出流stdout,2代表标准错误流stderror,这三个将是文件重定义的重要操作对象

#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);
}

额外知识:$$获取当前进程的PID

文件分配符的规则:从0开始在数组中寻找没有用过的数字下标作为新文件的文件分配符

现在分别关闭0,1来看看我说的对不对

 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);
}

#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;
}

 

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

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

dup2重定向接口

dup(复制文件描述符)

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

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

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

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

现在来看一下重定向的各种具体用法:

1.符号玩法,输出重定向:>

相似写法,结果相同的原因是因为>的左侧默认是1

 注意覆盖关系

2.dup2输出重定向(不推荐,本人感觉很麻烦)

#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
 
int main()
{
   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);
   fflush(stdout);
   close(fd);
 
   return 0;
}

3.符号玩法:追加重定向>>(不覆盖,一般玩这个)

在使用重定向时,可能会遇到各种问题。例如,不小心使用>而不是>>可能会导致原有文件内容的丢失。为了避免这种情况,我们应该在执行命令前仔细检查所使用的重定向操作符。此外,学习如何使用Shell脚本中的条件语句来检查文件是否存在,也可以帮助我们避免数据覆盖的问题 

4.dup2追加重定向 

#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;                                                                                                             
}

5.标准错误重定向(输入符号左边变为2)(封面黑猫的操作)

6.标准输入重定向< (将文件内容作为命令的输入)

注意read a与$ a 

6.dup2标准输入重定向

#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

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.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);//处理重定向符号
}

2.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);
        }
    }
}

3.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;
}

4.getpwdgetusername 和 gethostname1 是用于获取当前工作目录、用户名和主机名的占位符函数,应根据需要实现实际功能

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 ,程序替换,只会替换代码和数据,不影响曾经打开的重定向文件

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

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

不会

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

>

#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");

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

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

❗ 对于重定向的记忆

cat(cout)<<输出

>>(cin)输入到

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

linux一切皆文件的理解

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

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值