你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

Linux终端实现自己的命令解释器----mybash

2021-10-31 18:13:07

首先我们得知道Linux下产生新进程的过程是--->先复制---->再替换子进程(fork + exec)

来看代码  代码中都有解释的

下面代码实现的主要功能就是内置命令cd的简单操作和exit的作用

   #include <stdio.h>
   #include <assert.h>
   #include <stdlib.h>
   #include <string.h>
   #include <unistd.h>
   #include <sys/wait.h>
   #include <pwd.h>
   #include <dirent.h>
   
  char * get_cmd(char buff[],char * myargv[])
  {
      if(buff == NULL || myargv == NULL)
      {
          return NULL;
      }
  
      int i = 0;
      char * s = strtok(buff," ");//对命令进行分割 例如ps -f这种
      while( s!= NULL)
      {
          myargv[i++] = s;
          s = strtok(NULL," ");
      }
  
      return myargv[0];//识别输入的命令  作出对应的举动
  }
  
  
  void printf_info()  //这个函数的作用就是在执行mybash的时候前面那一行话
  {
      int id = getuid();//获取用户uid
      char* s = "$";
      if (id == 0)//当是管理员权限的时候 getuid()返回值是0
      {
          s = "#";
      }
  
      struct passwd * ptr = getpwuid(id);//根据uid返回用户真实信息
      if (ptr == NULL) //获取失败
      {
          printf("$");
          fflush(stdout);
          return;
      }
  
      char hostname[64] = {0};
      if(gethostname(hostname,64) == -1)
      {
          printf("$");
          fflush(stdout);
          return;
      }
  
      char pwd_buff[128] = {0};
      if(getcwd(pwd_buff,128) == NULL)
      {
          printf("$");
          fflush(stdout);
          return;
      }
      printf("%s@%s  %s:%s",ptr->pw_name,hostname,pwd_buff,s);
      fflush(stdout);
  
  }
  
  int main()
  {
      while(1)
      {
          char buff[128] = {0}; //存放键盘输入命令和参数
          printf_info();
  
          fgets(buff,128,stdin);//fgets会把\n也给读进去 
          buff[strlen(buff)-1] = 0; //这行就是将\n这个东西给去掉
  
  
          char * myargv[10] = {0};//这行相当于是命令库
          char * cmd = get_cmd(buff,myargv);
          if(cmd == NULL)//像原来系统的bash也是  没命令回车时会继续
          {
              continue;
          }
          else if(strcmp(cmd,"exit") == 0) //exit是内置命令 不需要产生新进程 也就不需要fork复制然后exec替换
          {
              break;
          }
  //这里说一下 内置命令和外部命令的区别  先说外部命令:需要产生一个新进程,所以就会fork+exec
  //内置命令就每这么麻烦 效率也更高  更快
          else if(strcmp(cmd,"cd") == 0) //cd也是内置命令
          {
              if (myargv[1] == NULL) //单纯的输入cd
              {
                  continue;
              }
              if (chdir(myargv[1]) == -1)//chdir错误之后的返回值就是-1   成功是0
              {
                  printf("cd err");
              }
              continue;
         }
         else //到这块else的话 就是外部命令了  需要fork+exec产生新进程
         {
             pid_t pid = fork();
             if(pid == -1)
             {
                 printf("fork err \n");
                 continue;
             }
             if(pid == 0)//这块就是运行mybash需要先对进程进行复制fork  然后exec进行替换  因为这是我们自己写的程序 需要产生新进程
             {
                 execvp(cmd,myargv);
                 printf("execvp err\n");//替换成功的话 不会执行到这一行的
                 exit(0);
             }
             wait(NULL);//将父进程阻塞住 并且也可以让子进程变成僵死进程
         }
     }
 }            

  在实现外部命令的时候是系统直接调用/usr/bin下的

也就是说在bash中执行外部命令的话   是和bash没什么关系的  都是从系统中调用的

下面再来自己编写个程序实现两个外部命令

pwd的程序实现:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
 
int main()
{
    char buff[128] = {0};
    if (getcwd(buff,128) != NULL)
    {
        printf("%s \n",buff);
    }
    return 0;
}

ls的程序实现

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
   
int main()
{
    char buff[128] = {0};
    if (getcwd(buff,128) == NULL)
    {
        return 0;
    }

    DIR * p = opendir(buff);
    if ( p == NULL)
    {
        return 0;
    }

    struct dirent * s = NULL;
    while((s = readdir(p)) != NULL)
    {
        if(strncmp(s->d_name,".",1)==0)
        {
           continue;
        }
        printf("%s \n",s->d_name);
    }

    closedir(p);
    return 0;

}