重定向介绍+简单模拟,fd的分配规则,dup2函数介绍+使用,命令行重定向,stdout和stderr的区别+分离不同信息+全部写入一个文件,shell模拟实现(添加重定向功能)

目录

重定向  

介绍

本质

fd的分配规则

引入

介绍 

重定向引入 

图示

追加重定向模拟

系统调用 -- dup2 

函数原型

输出重定向

 追加重定向

输入重定向

命令行重定向 

符号

拷贝文件内容 

stdout和stderr的区别

示例

应用

如何将打印的错误信息写入文件

如何将所有打印信息写入一个文件

./test > test.txt 2>&1

./test &> test.txt

在minishell中添加重定向功能 

思路

示例 

代码


重定向  

介绍

重定向是一种操作,可以将一个程序的输入或输出从标准位置(通常是终端)更改为其他地方,如文件或另一个程序

本质

但实际上我们了解fd后就可以知道,内部是使用fd来操作文件的,所以重定向需要改变fd相关数据

fd的分配规则

引入

当我们把fd=1的文件(也就是标准输出)关掉后,且以只读方式打开log.txt

会发现执行printf没有反应:

因为1对应的是标准输出的文件描述符,一旦关闭,1和终端就没有关联了(printf默认输出fd=1的文件)

若以写方式打开,其他不变

会发现log.txt的fd是1,写入的内容在log.txt中

这是为什么呢?

介绍 

实际上,分配fd是优先从最小开始找,找到的第一个没有被占用的就是该文件的描述符

  • 由于1关掉了(也就是被切断了与文件的联系,即数组中存放的指针变为null)
  • 当为log.txt份分配fd时,从0开始找
  • 0被stdin占用
  • 1的位置是空的
  • 所以log.txt的fd就是1了

计算机并不管数组中0,1,2的位置存放的谁的指针,只认fd

  • 因为它默认stdout的位置就在1所在位置放着的,所以printf就会直接向1指向的文件中写入
  • 而此时1已经是log.txt的fd了
  • 所以printf的内容就写入到log.txt中了

重定向引入 

图示

上面的例子总的来说,我们将本来应该写入显示器的内容,写入到了其他文件中

这不就是重定向吗!!!

追加重定向模拟

把选项换掉,就可以模拟出追加重定向 

上面的代码太挫了,得先把标准流关掉,再打开另一个文件

我们可以直接使用dup2函数来重定向

系统调用 -- dup2 

函数原型

  • 使newfd成为oldfd的副本
  • 也就是把oldfd中的内容拷贝到newfd中,最终两个空间都指向oldfd指向的文件结构体
  • 也就是说 -- 对newfd的操作,实际执行在oldfd的文件中

输出重定向

这里将标准输出重定向到log.txt中:

 追加重定向

比上面的代码增加一个追加项即可

输入重定向

将标准输入重定向到log.txt中,也就是让fgets从文件读取:

命令行重定向 

符号

拷贝文件内容 

先将test.txt内容作为cat命令的输入,再把cat的输出重定向到arr.txt中

stdout和stderr的区别

虽然他俩都对应显示器文件,但他俩是不同的

示例

 上面的代码,直接执行,似乎没有什么异常

但是,一旦进行重定向,只有stdout中的数据被重定向

也就是 -- 只有正确信息被重定向到文件,而错误信息还是打印到显示器上

应用

这样就可以在一堆打印中,分出正确的和错误的信息,来进行筛查

 

如何将打印的错误信息写入文件

./test > test.txt 是一种把前边的标准输出1忽略的写法

  • 它将test.txt的fd所在空间保存的指针拷贝进./test的标准输出的位置上
  • 这样就将标准输出重定向到test.txt中

2 > err.txt 是标准的语法,意思是将标准错误重定向到err.txt

  • 这样显式的写,可以让标准错误进行重定向
  • 重定向操作符的语法规则是 n > filename,其中n表示文件描述符,filename表示要重定向到的文件名
  • 若想要在filename的位置也表示文件描述符,可以在fd前加&,&会将fd解释为文件描述符,而不是文件名

如何将所有打印信息写入一个文件

前面的例子都是让两种信息分离,如何都放在一起呢?

./test > test.txt 2>&1

  • 可以理解为:先将stdout重定向到test.txt
  • 此时1的位置保存的就是test.txt的file*
  • 将2重定向到1
  • 也就是将2的位置也保存1的指针 -- test.txt的file*
  • 所以,1和2都指向test的file
./test &> test.txt

也可以写成 ./test &> test.txt

  • 表示将 标准输出stdout和标准错误输出stderr 都重定向至 指定的文件test.txt中
  • ./test >& test.txt同理

 

在minishell中添加重定向功能 

思路

重定向指令会带有>或<,所以可以直接判断里面是否存在这些符号,根据不同的符号,完成不同的操作

这里我们不识别空格

示例 

代码

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


#define num 1024
#define size 32
#define SEP " "
//设定重定向的标志码
#define WRITE 1
#define APPEND 2
#define INPUT 3

char cmd[num];
char* options[size];
int status; 

char* judge(char* start){
    char* end=start+strlen(cmd)-1; //指向最后一个有效字符
    while(end>=start){  
      if(*end=='>'){
        if(*(end-1)=='>'){ //">>"
          status=APPEND;
          *(end-1)='\0';  // xxx>>xxx 从第一个>开始截断
          end++;  //end指向重定向目的位置的文件名的第一个字符
                  //start指向要被重定向的文件名的第一个字符
          break;
        }
        else{
          status=WRITE;
          *end='\0';  //同理
          end++;
          break;
        }
      }
      else if(*end=='<'){
          status=INPUT;  
          *end='\0';  //同理
          end++;
          break;
      }
      else{
        end--;  //如果不是>,<,就往后轮
      }
    }
    if(end>=start){  //如果是没有执行完while,就break,就说明有重定向
      //exist redirect
      return end;  //返回文件名
    }
    else{
      return NULL;
    }
}

int main(){
  extern char** environ;
  while(1){
    printf("root@localhost myshell#");
    fflush(stdout);
    memset(cmd,'\0',sizeof cmd);
    if(fgets(cmd,sizeof cmd,stdin)==NULL){
      continue;
    }//ctrl+z to quit
    cmd[strlen(cmd)-1]='\0';

    //输入命令后,就可以先判断里面是否有重定向标志符号了
    char* situation=judge(cmd);//返回值为要重定向的文件名,如果没有,该变量为null

    options[0]=strtok(cmd,SEP); //拿到指令
    int i=1;
      if(strcmp(options[0],"ls")==0){
        options[i++]="--color=auto";
      }
    while(1){ //拿到后续的选项
      options[i++]=strtok(NULL,SEP);
      if(options[i-1]==NULL){
        break;
      }
    }
    if(strcmp(options[0],"export")==0){
      char* my_env=(char*)malloc(sizeof(char)*sizeof(options[1])); 
      strcpy(my_env,options[1]);
      putenv(my_env);
      continue;
    }

   // for(i=0;options[i];i++){ //用于测试
   //   printf("%s\n",options[i]);
   // }
   
    if(strcmp(options[0],"cd")==0){
      if(options[1]!=NULL){
        chdir(options[1]);  //change work_path
      }
      continue;
    }

    pid_t id=fork();
    if(id==0){
      int fd=0;
      if(status!=0){  //如果存在重定向,则status不为0
        switch(status){
          case WRITE:
            fd=open(situation,O_WRONLY|O_TRUNC|O_CREAT,0666);
            dup2(fd,1); //将对1的操作执行到fd中
            break;
          case APPEND:
            fd=open(situation,O_WRONLY|O_APPEND|O_CREAT,0666);
            dup2(fd,1);
            break;
          case INPUT:
            fd=open(situation,O_RDONLY);
            dup2(fd,0);
            break;
          default:
            printf("status fail\n");
            break;
        }
      }
        //如果没有重定向,普通执行命令即可
      execvpe(options[0],options,environ);
      exit(1);
    }
    else{
      int status=0;
      pid_t ret=waitpid(-1,&status,0);
      if(ret>0){  //success
        //printf("%s:%d\n",options[0],WEXITSTATUS(status));
      }
      else{   //fail
        printf("fail\n");
      }
    }
  }
  return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值