模拟Linux的shell

在学习了Linux的进程控制之后,学习了fork函数和exec函数族,通过这些个函数可以简单的实现一份shell,就是实现一份命令行解释器,当然是简单版的,实现功能如下

  1. 能执行普通的命令如ls ,ps ,top等
  2. 可以实现目录的跳转cd命令
  3. 能执行命令并加上参数如ls-l
  4. 能执行打开man手册
  5. 能识别管道符

还不能实现正则表达式,要实现这个我当前的代码根本不能用,要重头开始改写。。。

下面贴代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <pwd.h>
#include <sys/utsname.h>
#include <libgen.h>


void eatblank(char **buf)
{
  while(**buf==' ')
  {
    (*buf)++;
  }
}


void GetHostName(char *hostname,int length)
{
  gethostname(hostname,length);
  char *p=hostname;
  while(*p!='\0')
  {
    if(*p=='.')
    {
      *p='\0';
    }
    p++;
  }
}

void Pipe(char **my_argv,char *buf);
void BuildCommand(char **my_argv,char *buf)
{
  eatblank(&buf);
  my_argv[0]=buf;
  int index=1;
  char *p=buf;
      while(*p!='\0')
      {
        if(*p==' ')
        {
          *p='\0';
          p++;
          eatblank(&p) ;
          if(*p!='|')
          {
            my_argv[index++]=p;
          }
          continue;
        }
        else if(*p=='|')
        {
          p++;
          //p++;
          my_argv[index]=NULL;
          Pipe(my_argv,p);
        }
        else
        {
          p++;
        }
      }
     my_argv[index]=NULL;
}



void Pipe(char ** my_argv,char *buf)
{
  int fd[2];
  pipe(fd);
  pid_t id2=fork();
  if(id2==0)
  {
    close(1);
    dup(fd[1]);
    close(fd[1]);
    close(fd[0]);
    execvp(my_argv[0],my_argv);
  }
  else
  {
    waitpid(id2,NULL,0);
    close(0);
    dup(fd[0]);
    close(fd[0]);
    close(fd[1]);
    BuildCommand(my_argv,buf);
    execvp(my_argv[0],my_argv);
  }
  //在此处添加exec族函数
}


int main()
{
  while(1)
  {
    char *my_argv[64];
      struct passwd *pwd=getpwuid(getuid());
      char hostname[256]={'\0'};
      char cwd[256]={'\0'};
      getcwd(cwd,256);
      GetHostName(hostname,256);
      printf("[%s@%s %s]#",pwd->pw_name,hostname,basename(cwd));
      fflush(stdout);
      char buf[1024];
      buf[0]='\0';

      int count=read(0,buf,sizeof(buf));
      buf[count-1]='\0';
      my_argv[0]=buf;    
    pid_t id=fork();
    if(id==0)
    {
      //child
     
      if(strncmp(buf,"cd",2)==0) 
      {
        exit(1);
      }
      BuildCommand(my_argv,buf);
     execvp(my_argv[0],my_argv);
     printf("if the process has some problem ,I should run here\n");
     exit(0);
    }
    else
    {
      //father
      int status=0;
      wait(&status);
      if(status==256)
      {
        my_argv[0]+=3;
        chdir(my_argv[0]);
      }
    }
  }
  return 0;
}


通过gethostname获取主机名,通过getcwd获得当前工作目录,通过getpwuid获得当前登录的用户信息

这样命令提示符就完成了;

  • 普通命令的实现

普通命令的实现并不困难,我的目的是让子进程来执行命令,也就是通常的让fork产生的子进程执行exec族函数,然后自己死掉,通过父进程回收资源,循环并创建新的子进程,这就是shell的大框架,其中用execvp函数来实现命令的执行,函数原型就不多说了,查手册就能查到,简单解释一下参数,第一个参数是命令行的字符串,第二是参数是一个字符串数组,从上到下依次存放,命令,参数(可能有多个,一个参数占一个下标),最后用NULL占据一个下标表示结束。

  • cd命令的实现

cd命令的实现有些问题,不是普通命令的实现,就是说简单的使用execvp是不能实现的,因为就算子进程改变了目录之后也会把自己杀死,父进程和子进程之间是不通的(就是说父进程和子进程并不共享环境变量,子进程修改了当前工作目录的环境变量对父进程也没有什么影响),后来使用system来执行系统调用,也失败,因为更多时候shell会产生一个子进程来执行命令,因为shell本身执行会有风险,可能会因为用户的错误操作把自己挂掉,所以使用子进程来执行命令,而这样和刚才的结果是一样的并不会影响到父进程,最后采用了chdir函数,他可以改变当前进程的环境变量中的工作目录(就是专门change dir用的),让父进程来执行,fork出来的子进程会有一份父进程环境变量的拷贝,这就完成了改变目录,并将结果传递了下来

else
   {
     //father
     int status=0;
     wait(&status);
     if(status==256)
     {
       my_argv[0]+=3;
       chdir(my_argv[0]);
     }
   }

  • 管道符的实现

管道符的实现很简单,平常我们执行命令的时候,都是结果自动输出到电脑屏幕上,这说明一般命令的输出是write在标准输出stdout的,而我们输出命令的参数是通过键盘,这说明命令的输入来源是标准输入stdin,如果我们关闭了,标准输出和标准输入,而是通过一个管道,让结果写进管道,然后让参数从管道中读取(简单的说就是让管道的两段代替标准输入和标准输出),管道符就实现了

void Pipe(char ** my_argv,char *buf)
{
  int fd[2];
  pipe(fd);
  pid_t id2=fork();
  if(id2==0)
  {
    close(1);
    dup(fd[1]);
    close(fd[1]);
    close(fd[0]);
    execvp(my_argv[0],my_argv);
  }
  else
  {
    waitpid(id2,NULL,0);
    close(0);
    dup(fd[0]);
    close(fd[0]);
    close(fd[1]);
    BuildCommand(my_argv,buf);
    execvp(my_argv[0],my_argv);
  }
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值