码迷,mamicode.com
首页 > 系统相关 > 详细

linux下编写一个仿shell

时间:2016-06-26 00:40:31      阅读:299      评论:0      收藏:0      [点我收藏+]

标签:linux   进程   父进程   execvp   编写shell   

1、首先了解shell的基本框架

技术分享

如上图所示

[用户名@主机名 当前路径]$ 命令

执行命令结果

目标:完成一个简单的shell(输入命令可以得到执行结果)

所以框架分为:

1、【提示符】$的显示 -----一堆函数的调用即可

2、  命令的执行-----读入数据,进行解析,得到argv[],执行execvp

3、  对于内置命令cd的单独处理

#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
void prompt()
{
  struct passwd * p1;
  char username[64];
  char hostname[256];
  char pathname[1024];
  //获取用户名
  p1=getpwuid(getuid());
  //获取路径
  getcwd(pathname,sizeof(pathname));
  //获取主机名---返回0正确
  if(gethostname(hostname,sizeof(hostname))==0)
  {
    printf("[DIY %s@%s:%s ]",p1->pw_name,hostname,pathname);
  }
  else
  {
    printf("[DIY %s@unkown:%s ]",p1->pw_name,pathname);
  }
  if(geteuid()==0)
  {
    printf("#");
  }
  else
  {
    printf("$");
  }
}
int main()
{
  while(1)
  {
    prompt();
    fflush(stdout);
    //获得环境变量
    char buf[1024];
    memset(buf,0,sizeof(buf));
    size_t size=read(0,buf,sizeof(buf)-1);
    
    if(size>0)
    {
      buf[size-1]=‘\0‘;
    }
   // printf("%s\n",buf);
    char *p=buf;
    int i=0;
    char * my_argv[64]={0};
    my_argv[0]=p;
    while(*p!=0)
    {
      if(*p==‘ ‘)
      {
        *p=‘\0‘;
        ++p;
        my_argv[++i]=p;
      }
      else
      {
        ++p;
      }
    }
    pid_t id=fork();
    
    if(id==0) //子进程
    {
      
      if(strcmp(my_argv[0],"cd")==0)
      {
        chdir(my_argv[1]);
      }
      else
      {
        execvp(my_argv[0],my_argv);
      }
    }
    else  //父进程
    {
      pid_t ret=waitpid(id,NULL,0);
    }
  }
  return 0;
}

技术分享

这样看好像一切都辣么完美~~~

然而却没办法退出,除非按control+z键强行退出

于是,我想是不是exit也可以实现

    if(id==0)
    {
     if(strcmp(my_argv[0],"exit")==0)
     {
       exit(0);
     }
     ...
    }
    else
    {
      pid_t ret=waitpid(id,NULL,0);
      if(strcmp(my_argv[0],"exit")==0)
      {
       exit(0);
      }
    }
  }
  return 0;
}

结果是:

技术分享

然而接下来这个exit做出的事情就比较神奇了

技术分享

用了两次cd,pwd操作,然而exit却要执行三次才可以退出,这是为什么呢?

于是为了调试我将所有指令执行的进程的id及其父进程的id进行了打印分析

技术分享技术分享


结论:

ls:第一次创建的子进程在执行完execvp之后终止

cd..第二次创建的子进程在执行chdir并不会终止

cd..第三次创建的子进程其父进程是第二次创建的子进程且不会终止

当执行exit,会从里到外一层一层的终止,然后才退出整个程序

改进方案:

将cd执行的函数写入到父进程

#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
void prompt()
{
  struct passwd * p1;
  char username[64];
  char hostname[256];
  char pathname[1024];
  //获取用户名
  p1=getpwuid(getuid());
  //获取路径
  getcwd(pathname,sizeof(pathname));
  //获取主机名---返回0正确
  if(gethostname(hostname,sizeof(hostname))==0)
  {
    printf("[DIY %s@%s:%s ]",p1->pw_name,hostname,pathname);
  }
  else
  {
    printf("[DIY %s@unkown:%s ]",p1->pw_name,pathname);
  }
  if(geteuid()==0)
  {
    printf("#");
  }
  else
  {
    printf("$");
  }
}
int main()
{
  while(1)
  {
    prompt();
    fflush(stdout);
    char buf[1024];
    memset(buf,0,sizeof(buf));
    size_t size=read(0,buf,sizeof(buf)-1);
    
    if(size>0)
    {
      buf[size-1]=‘\0‘;
    }
   // printf("%s\n",buf);
    char *p=buf;
    int i=0;
    char * my_argv[64]={0};
    my_argv[0]=p;
    while(*p!=0)
    {
      if(*p==‘ ‘)
      {
        *p=‘\0‘;
        ++p;
        my_argv[++i]=p;
      }
      else
      {
        ++p;
      }
    }
    pid_t id=fork();
    if(id==0)
    {
     if(strcmp(my_argv[0],"exit")==0)
      {
       exit(0);
      }
      if(strcmp(my_argv[0],"cd")==0)
      {
         exit(0);
      }
      else
      {
        execvp(my_argv[0],my_argv);
      }
    }
    else
    {
      pid_t ret=waitpid(id,NULL,0);
     if(strcmp(my_argv[0],"cd")==0)
       {
        chdir(my_argv[1]);
       }
      if(strcmp(my_argv[0],"exit")==0)
      {
       exit(0);
      }
    }
  }
  return 0;
}

结果:

技术分享

本文出自 “momo就是辣么萌” 博客,请务必保留此出处http://momo462.blog.51cto.com/10138434/1792908

linux下编写一个仿shell

标签:linux   进程   父进程   execvp   编写shell   

原文地址:http://momo462.blog.51cto.com/10138434/1792908

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!