歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> 學習Linux >> 模擬Linux的shell,模擬Linuxshell

模擬Linux的shell,模擬Linuxshell

日期:2017/3/6 9:34:58   编辑:學習Linux

模擬Linux的shell,模擬Linuxshell


模擬Linux的shell,模擬Linuxshell


在學習了Linux的進程控制之後,學習了fork函數和exec函數族,通過這些個函數可以簡單的實現一份shell,就是實現一份命令行解釋器,當然是簡單版的,實現功能如下

還不能實現正則表達式,要實現這個我當前的代碼根本不能用,要重頭開始改寫。。。

下面貼代碼

  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   }

http://xxxxxx/Linuxjc/1137425.html TechArticle

Copyright © Linux教程網 All Rights Reserved