模拟Linux的shell

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

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

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

下面贴代码

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <string.h>
 4 #include <sys/types.h>
 5 #include <sys/wait.h>
 6 #include <stdlib.h>
 7 #include <pwd.h>
 8 #include <sys/utsname.h>
 9 #include <libgen.h>
 10 
 11 
 12 void eatblank(char **buf)  13 {  14   while(**buf==' ')  15  {  16     (*buf)++;  17  }  18 }  19 
 20 
 21 void GetHostName(char *hostname,int length)  22 {  23  gethostname(hostname,length);  24   char *p=hostname;  25   while(*p!='\0')  26  {  27     if(*p=='.')  28  {  29       *p='\0';  30  }  31     p++;  32  }  33 }  34 
 35 void Pipe(char **my_argv,char *buf);  36 void BuildCommand(char **my_argv,char *buf)  37 {  38   eatblank(&buf);  39   my_argv[0]=buf;  40   int index=1;  41   char *p=buf;  42       while(*p!='\0')  43  {  44         if(*p==' ')  45  {  46           *p='\0';  47           p++;  48           eatblank(&p) ;  49           if(*p!='|')  50  {  51             my_argv[index++]=p;  52  }  53           continue;  54  }  55         else if(*p=='|')  56  {  57           p++;  58           //p++;
 59           my_argv[index]=NULL;  60  Pipe(my_argv,p);  61  }  62         else
 63  {  64           p++;  65  }  66  }  67      my_argv[index]=NULL;  68 }  69 
 70 
 71 
 72 void Pipe(char ** my_argv,char *buf)  73 {  74   int fd[2];  75  pipe(fd);  76   pid_t id2=fork();  77   if(id2==0)  78  {  79     close(1);  80     dup(fd[1]);  81     close(fd[1]);  82     close(fd[0]);  83     execvp(my_argv[0],my_argv);  84  }  85   else
 86  {  87     waitpid(id2,NULL,0);  88     close(0);  89     dup(fd[0]);  90     close(fd[0]);  91     close(fd[1]);  92  BuildCommand(my_argv,buf);  93     execvp(my_argv[0],my_argv);  94  }  95   //在此处添加exec族函数
 96 }  97 
 98 
 99 int main() 100 { 101   while(1) 102  { 103     char *my_argv[64]; 104       struct passwd *pwd=getpwuid(getuid()); 105       char hostname[256]={'\0'}; 106       char cwd[256]={'\0'}; 107       getcwd(cwd,256); 108       GetHostName(hostname,256); 109       printf("[%s@%s %s]#",pwd->pw_name,hostname,basename(cwd)); 110  fflush(stdout); 111       char buf[1024]; 112       buf[0]='\0'; 113 
114       int count=read(0,buf,sizeof(buf)); 115       buf[count-1]='\0'; 116       my_argv[0]=buf; 117     pid_t id=fork(); 118     if(id==0) 119  { 120       //child
121      
122       if(strncmp(buf,"cd",2)==0) 123  { 124         exit(1); 125  } 126  BuildCommand(my_argv,buf); 127      execvp(my_argv[0],my_argv); 128      printf("if the process has some problem ,I should run here\n"); 129      exit(0); 130  } 131     else
132  { 133       //father
134       int status=0; 135       wait(&status); 136       if(status==256) 137  { 138         my_argv[0]+=3; 139         chdir(my_argv[0]); 140  } 141  } 142  } 143   return 0; 144 }

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

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

  • 普通命令的实现

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

  • cd命令的实现

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

 1  else
 2  {  3       //father
 4       int status=0;  5       wait(&status);  6       if(status==256)  7  {  8         my_argv[0]+=3;  9         chdir(my_argv[0]); 10  } 11     }

 

  • 管道符的实现

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

 1 void Pipe(char ** my_argv,char *buf)  2 {  3   int fd[2];  4  pipe(fd);  5   pid_t id2=fork();  6   if(id2==0)  7  {  8     close(1);  9     dup(fd[1]); 10     close(fd[1]); 11     close(fd[0]); 12     execvp(my_argv[0],my_argv); 13  } 14   else
15  { 16     waitpid(id2,NULL,0); 17     close(0); 18     dup(fd[0]); 19     close(fd[0]); 20     close(fd[1]); 21  BuildCommand(my_argv,buf); 22     execvp(my_argv[0],my_argv); 23   }

 

你可能感兴趣的:(模拟Linux的shell)