文件fd【shell完善】

文件内有3个特定的文件标识符,stdin,stdout,stderr所以在我们打印fd的时候会发现从3开始,不过当我们close的时候会有如下发现

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#define FILE_NAME(number) "log.txt" #number
int main()
{
//第一次执行0
    close(0);
//第二次执行2
  //close(2);
//第三次执行1
    //close(1);
    int fd0=open(FILE_NAME(1),O_WRONLY|O_CREAT|O_APPEND,0666);
    printf("%d\n",fd0);
    return 0;
}

执行close(0)以后:

把stdin(键盘)即fd=0(键盘)改到文件,从文件输入内容,然后输出到显示器中

在这里插入图片描述

执行close(2)以后:

在这里插入图片描述

以上我们可以得到一个规律,即那一个被close掉,fd就会去占用它

执行close(1)以后:

这个需要我们查看log.txt1,具体原因在于fd即log.txt1占用了stdout,把原本输出到显示器中的内容输出到了文件中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q36cjsE2-1680793083494)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/62ef5545-a9c2-4163-bd8b-d12a0741ed80/Untitled.png)]

在实际使用中我们通常使用dup2这个系统调用(不过这个fd输出依旧是3),这个就叫做重定向

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#define FILE_NAME(number) "log.txt" #number
int main()
{
int fd0=open(FILE_NAME(1),O_WRONLY|O_CREAT|O_APPEND,0666);
dup2(fd0,0);
printf("%d\n",fd0);
return 0;
}

那么重定向的功能就可以把shell再完善一下,这样就可以支持ls -l > log.txt ls-l >> log.txt cat < log.txt的功能

#include<stdio.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<ctype.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define NUM 1024
char LineCommand[NUM];
#define OPT_NUM 64
char* myargv[OPT_NUM];//指针数组
int lastcode=0;
int lastsign=0;
//普通
#define NONE_REDIR 0
//输入重定向
#define INPUT_REDIR 1
//输出重定向
#define OUTPUT_REDIR 2
//追加重定向
#define APPEND_REDIR 3
#define trimSpace(start) do{ while(isspace(*start))  ++start; }while(0)
//用来确认是什么重定向,默认没有
int redirType = NONE_REDIR;
char* redirFile=NULL;
void commandCheck(char* command)
{
  char* start=command;
  char* end=command+strlen(command);
  while(start<end)
  {
    if(*start=='>')
    {
      *start='\0';
      ++start;
      if(*start=='>')

      {
        //有两个>>是追加重定向
        redirType=APPEND_REDIR;
        ++start;
      }
      //否则只有一个,是输出重定向
      else
      {
        redirType=OUTPUT_REDIR;
      }
      //跳过空格
      trimSpace(start);
      redirFile=start;
      break;
    }
    else if(*start=='<')
    {
      redirType=INPUT_REDIR;
      *start='\0';
      ++start;
      trimSpace(start);
      redirFile=start;
      break;
    }
    ++start;
  }
}
int main()
{
  while(1)
  {
    char LineCommand[NUM]={0};
    char* myargv[OPT_NUM]={NULL};
    //输出提示符
    printf("用户名@主机 当前路径# ");
    //刷新缓冲区,因为没有\n不会刷新缓冲区
    fflush(stdout);
    //获取用户输入,预留一个\0,所以需要-1
    char* s=fgets(LineCommand,sizeof(LineCommand)-1,stdin);
    assert(s!=NULL);
     保证在release方式发布的时候,因为去掉assert了,所以s就没有被使用,而带来的编译告警, 什么都没做,但是充当一次使用
    (void)s;
    //这里会把用户的\n也输入进去,就会有2个\n
    //清楚最后一个\n
    LineCommand[strlen(LineCommand)-1]='\0';
    //printf("test:%s\n",LineCommand);
    //分割,例如ls -l > log.txt 会被分割成ls -l \0 log.txt stroke读到\0结束
    commandCheck(LineCommand);
    //我们输入的是
    //"ls -a -l" 但是系统想要读取的是"ls" "-a" "-l"
    //字符串切割
    //用空格切割
    myargv[0]=strtok(LineCommand," ");
    int i=1;
    if(myargv[0]!=NULL&&strcmp(myargv[0],"ls")==0)
    {
      myargv[i++]="--color=auto";
    }
    //如果没有子串strtok会返回NULL
    //而我们的exec*系列的函数需要以NULL结束
    while(myargv[i++]=strtok(NULL," "));
    //子进程运行结束以后,继续用的还是父进程,即shell
    if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)
    {
      //我们执行cd ..没有用,原因在于我们改变的是子进程的目录,父进程的目录没有改变
      //这种指令需要我们父进程自己执行,这种指令叫做内置/内建指令
      if(myargv[1]!=NULL)
      {
        chdir(myargv[1]);
        continue;
      }
    }
    if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0)
    {
      if(strcmp(myargv[1],"$?")==0)
      {
        printf("%d,%d\n",lastcode,lastsign);
      }
      else
      {
        printf("%s\n",myargv[1]);
      }
      continue;
    }
    //条件编译
#ifdef DEBUG
    for(int i=0;myargv[i];++i)
    {
      printf("myargv[%d]:%s\n",i,myargv[i]);
    }
#endif
    //执行命令一般给子进程执行
    pid_t id=fork();
    assert(id>=0);
    (void)id;
    if(id==0)
    {
      //child
      switch(redirType)
      {
           case NONE_REDIR : break;
           case OUTPUT_REDIR : {int fd=open(redirFile,O_WRONLY|O_CREAT|O_TRUNC,0666); dup2(fd,1); break;}
           case APPEND_REDIR : {int fd=open(redirFile,O_WRONLY|O_CREAT|O_APPEND,0666); dup2(fd,1); break;}
           case INPUT_REDIR : {int fd=open(redirFile,O_RDONLY); dup2(fd,0);break;}
           default: printf("错误\n"); break;
      }

      execvp(myargv[0],myargv);
      //只要返回一定失败
      exit(-1);
    }
    int status=0;
    pid_t ret = waitpid(id,&status,0);
    lastcode=(status>>8)&0xff;
    lastsign=status&0x7f;
}
  return 0;
}

linux下一切皆文件
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值