UIUC 系统编程 assignment 1 simple shell

完成了一个简单的shell,完成了assignment提到的所有功能,

执行非built in 指令,执行built in 指令 1.cd  2. exit  3 !# 类似history !#N 显示最近N条指令。  !3执行历史记录3的命令。

核心就是fork exec wait 不过纯用c感觉比较烦,资源需要小心管理,难于控制复杂程度,代码也仅仅是完成要求的基本功能,如果加入更多功能代码就更加容易出错,在记录历史和输入的顺序上处理的不太好,当前输入后counter就++了,应该先输入,然后分析命令,然后执行,如果historyf命令出错不加入历史,否则加入历史命令列表,counter++.

下一步考虑改用c++实现。

 测试如下

allen:~/study/system_programming/uiuc_assignment/smp1$ ./shell
Shell(pid=16153)1:
Child failed to exec: No such file or directory
Shell(pid=16153)2:
allen:~/study/system_programming/uiuc_assignment/smp1$ ./shell
Shell(pid=16159)1:ls
CS241.txt  Makefile  README.pdf  README.txt  shell  shell2  shell2.c  shell2.cc  shell.c  shell.cc  shell.h  shell.o  smp1.zip svn-commit.tmp TEAM.txt  test
Shell(pid=16159)2:/bin/ls
CS241.txt  Makefile  README.pdf  README.txt  shell  shell2  shell2.c  shell2.cc  shell.c  shell.cc  shell.h  shell.o  smp1.zip svn-commit.tmp TEAM.txt  test
Shell(pid=16159)3:pwd
/home/allen/study/system_programming/uiuc_assignment/smp1
Shell(pid=16159)4:cd ..
Shell(pid=16159)5:pwd
/home/allen/study/system_programming/uiuc_assignment
Shell(pid=16159)6:cd smp1
Shell(pid=16159)7:ls
CS241.txt  Makefile  README.pdf  README.txt  shell  shell2  shell2.c  shell2.cc  shell.c  shell.cc  shell.h  shell.o  smp1.zip svn-commit.tmp TEAM.txt  test
Shell(pid=16159)8:ls -l
总用量 236
-rwxr-xr-x 1 allen allen   8064 2009-07-14 16:41 CS241.txt
-rwxr-xr-x 1 allen allen    182 2009-07-17 10:41 Makefile
-rwxr-xr-x 1 allen allen 119738 2009-07-14 16:41 README.pdf
-rwxr-xr-x 1 allen allen   7429 2009-07-14 16:41 README.txt
-rwxr-xr-x 1 allen allen  16764 2009-07-17 17:54 shell
-rwxr-xr-x 1 allen allen  11764 2009-07-17 12:39 shell2
-rw-r--r-- 1 allen allen   3727 2009-07-17 12:39 shell2.c
-rwxr-xr-x 1 allen allen   3197 2009-07-17 11:01 shell2.cc
-rwxr-xr-x 1 allen allen   8516 2009-07-17 17:54 shell.c
-rw-r--r-- 1 allen allen   3475 2009-07-16 21:54 shell.cc
-rw-r--r-- 1 allen allen    943 2009-07-16 22:02 shell.h
-rw-r--r-- 1 allen allen  11748 2009-07-17 17:54 shell.o
-rwxr-xr-x 1 allen allen   7296 2009-07-14 16:41 smp1.zip
-rw-r--r-- 1 allen allen     57 2009-07-15 10:45 svn-commit.tmp
-rwxr-xr-x 1 allen allen    560 2009-07-14 16:41 TEAM.txt
drwxr-xr-x 2 allen allen   4096 2009-07-17 11:32 test
Shell(pid=16159)9:cd shell
shell is not a directory
Shell(pid=16159)10:cd dfsdf
Cannot stat dfsdf: No such file or directory
Shell(pid=16159)11:!#
 !  1 ls 
 !  2 /bin/ls 
 !  3 pwd 
 !  4 cd .. 
 !  5 pwd 
 !  6 cd smp1 
 !  7 ls 
 !  8 ls -l 
 !  9 cd shell 
 ! 10 cd dfsdf 
 ! 11 !# 
Shell(pid=16159)12:!#3
 ! 10 cd dfsdf 
 ! 11 !# 
 ! 12 !#3 
Shell(pid=16159)13:!9
cd shell 
shell is not a directory
Shell(pid=16159)14:!#
 !  1 ls 
 !  2 /bin/ls 
 !  3 pwd 
 !  4 cd .. 
 !  5 pwd 
 !  6 cd smp1 
 !  7 ls 
 !  8 ls -l 
 !  9 cd shell 
 ! 10 cd dfsdf 
 ! 11 !# 
 ! 12 !#3 
 ! 13 cd shell 
 ! 14 !# 
Shell(pid=16159)15:exit
allen:~/study/system_programming/uiuc_assignment/smp1$ 

 

shell.c

  1 /* SMP1:Simple Shell */
  2 // to do 可以引入vector 管理,vector < vector < char*> > 不要用string因为c api接口问题
  3 /* LIBRARY SECTION */
  4 #include <ctype.h>              /* Character types                       */
  5 #include <stdio.h>              /* Standard buffered input/output        */
  6 #include <stdlib.h>             /* Standard library functions            */
  7 #include <string.h>             /* String operations                     */
  8 #include <sys/types.h>          /* Data types                            */
  9 #include <sys/wait.h>           /* Declarations for waiting              */
 10 #include <unistd.h>             /* Standard symbolic constants and types */
 11 #include <sys/stat.h>           /* wee need stat */
 12 /* DEFINE SECTION */
 13 #define SHELL_BUFFER_SIZE 256   /* Size of the Shell input buffer        */
 14 const int CommandsNum = 10//初始commands_vec 的容量
 15 static int capacity;    //当前commands_vec的最大容量,会根据指令数目超出后自动增加容量每次*2,模拟vector
 16 
 17 static pid_t pid;   //shell 进程id
 18 static int counter; //指令计数,从0开始,当用户输入一个命令,counter++,当前命令应为counter - 1,注意print的时候从1开始
 19 static char ***commands_vec;    //记录所有的命令,commands_vec[0]表示第0个命令,该命令会由多个args构成(空格已去)
 20 
 21 // 初始化,得到进程id,counter置0.. 
 22 void Init() 
 23 {
 24     pid = getpid();
 25     counter = 0;
 26     commands_vec = (char ***) malloc (CommandsNum * sizeof(char **));
 27     capacity = CommandsNum;
 28 }
 29 
 30 //打印命令,所有的args
 31 void EchoArgs(char **args) 
 32 {
 33     while(*args) {
 34         printf("%s "*args);
 35         args++;
 36     }
 37     printf("\n");
 38 }
 39 
 40 //释放一个命令所占的内存资源
 41 void FreeResouce(char **args) 
 42 {
 43     if (!args)
 44         return;
 45     char **arg = args;
 46     while(*arg) {
 47         free(*arg);
 48         *arg = 0;
 49         arg++;
 50     }
 51     free(args);
 52 }
 53 
 54 //是否历史记录的所有命令所占资源
 55 void FreeComandsResouce()
 56 {
 57     int i;
 58     for (i = 0; i < counter; i++) {
 59         FreeResouce((char **)commands_vec[i]);   
 60         commands_vec[i] = NULL;     //do not forget to make it NULL
 61     }
 62     free(commands_vec);
 63     commands_vec = NULL;
 64 }
 65 
 66 //将命令arglist加入命令历史列表中,counter计数+1,如需要增加
 67 //历史记录列表的容量,直接调用realloc更好
 68 void AddHistory(char **arglist) 
 69 {
 70     commands_vec[counter++= arglist; 
 71     if (counter == capacity) {
 72         char ***tmp;
 73         tmp = (char ***) malloc (2 * capacity * sizeof(char **));
 74         int i;
 75         for (i = 0; i < capacity; i++) {
 76             tmp[i] = commands_vec[i];
 77             commands_vec[i] = NULL;
 78         }
 79         //memset(tmp + capacity, NULL, capacity * sizeof(char **));
 80         free(commands_vec);
 81         commands_vec = tmp;
 82         capacity *= 2;
 83     } 
 84 }
 85 
 86 //打印提示信息,接受用户输入的命令,并进行预处理去掉空格存储,
 87 //然后将命指针加入历史记录列表,返回当前命令指针
 88 char** GetInput() 
 89 {
 90     char input[SHELL_BUFFER_SIZE];
 91     //char *arglist[SHELL_BUFFER_SIZE];
 92     char** arglist;
 93     arglist = (char **) malloc(SHELL_BUFFER_SIZE * sizeof(char *));
 94     //memset(arglist, NULL, SHELL_BUFFER_SIZE * sizeof (char *));
 95     int i = 0;
 96     int j = 0;
 97     int start = 0;
 98     char c;
 99     
100     //打印提示信息
101     printf("Shell(pid=%d)%d:",pid,counter + 1);
102 
103     //接受用户输入command,并且将各个命令参数存储起来,去空格识别
104     while (i < SHELL_BUFFER_SIZE && (c = getchar()) != '\n') {
105         if (c == ' ') { 
106             if (input[i - 1!= ' ') {
107                 arglist[j] = (char *) malloc(i - start + 1);
108                 strncpy(arglist[j], input + start, i - start);
109                 arglist[j][i - start] = '\0';
110                 j++;
111                 start = i + 1;
112             } else {
113                 start++;
114             }
115         }
116         
117         input[i++= c;
118     }
119     arglist[j] = (char *) malloc(i - start + 1);
120     strncpy(arglist[j], input + start, i - start);
121     arglist[j][i - start] = '\0';
122     j++;
123     arglist[j] = NULL;  //标志结束
124 
125     if (i == SHELL_BUFFER_SIZE) {   //错误处理,输入的command大小要小于256包括空格
126         while(c = getchar() != '\n')  //需要把剩下的输入数据读完!
127                 ;
128         printf("Commands should be withen the size of 256\n");
129         FreeResouce(arglist);
130         return NULL;
131     }
132 
133     AddHistory(arglist);
134 
135     return arglist;
136 }
137 
138 //通过fork新开子进程执行non built in 命令
139 //通过主进程wait
140 void ExcuteNonBuiltinCommand(char **args)
141 {
142     pid_t p_id;
143     p_id = fork();
144     
145     if (p_id == -1) {
146         perror("Failed to fork");
147         return;
148     }
149 
150     //child code
151     if (p_id == 0) {   
152         if (strstr(args[0],"/"))  //如果命令中找到/,则按照绝对路径寻找
153             execv(args[0],args);
154         else    //find command from PATH
155             execvp( args[0], args );
156         
157         perror("Child failed to exec");
158         exit(1);    //注意不要用return 子进程应该结束,即使它失败了,exit(1) not exit(0)
159     } 
160 
161     //parent code p_id != 0
162     if (wait(NULL) != p_id) {
163         perror("Parent failed to wait for child");
164     }
165 
166 }
167 
168 //打印最近n个命令历史记录
169 void PrintCommandHistory(int n) {
170     int i;
171     for (i = counter - n; i < counter; i++) {
172         printf(" !%3d ", i + 1);
173         EchoArgs(commands_vec[i]);
174     }
175 }
176 
177 // 删除当前命令历史记录,因为按照当前的处理流程,用户输入后命令就被记录
178 // 对于需要删除不执行并且不记录的命令,需要将其删除
179 void DeleteCurrentCommand()
180 {
181     free(commands_vec[counter - 1]);
182     commands_vec[counter - 1= NULL;
183     counter--;
184 }
185 
186 int IsNum(char a)
187 {
188     if (a >= '0' && a <= '9')
189         return 1;
190     else
191         return 0;
192 }
193 //给定字符串中一个起始一个结束位置,返回所对应的数字大小,只处理正数
194 //如果出现非数字视为出错则返回-1,调用者确保arg,i, j的正确性
195 int GetNumFromString(char *arg, int start, int end)
196 {
197     int i;
198     int base = 1;
199     int sum = 0;
200     for(i = end; i >= start; i--) {
201         if(!IsNum(arg[i]))
202             return -1;
203         sum += (arg[i] - '0'* base;
204         base *= 10;
205     }
206     return sum;
207 }
208 
209 //执行命令,区别built in 和 non built in
210 //built in 当前支持 cd, exit, !#, !,
211 //non built in 调用ExcuteNonBuiltinCommand
212 void ExcuteCommand(char **args) 
213 {
214     if (!args)
215         return;
216 
217     /* built in command */
218     
219     /* cd */
220     char * str = "cd";
221     if (strcmp(args[0], str) == 0) {
222        
223         struct stat info;
224 
225         /*if no such file or dir */
226         if ( stat( args[1] , &info ) == -1 ){ 
227             fprintf(stderr, "Cannot stat ");
228             perror(args[1]);
229             return;
230         }
231         if(!S_ISDIR(info.st_mode)) {
232             fprintf(stderr, "%s is not a directory\n", args[1]);
233             return;
234         }
235         
236         /*now ok to cd*/
237         chdir(args[1]);
238         return;
239     }
240     
241     /* exit */
242     str = "exit";
243     if (strcmp(args[0], str) == 0) {
244         FreeComandsResouce();
245         exit(0);
246         return;
247     }
248    
249     /* !#  print history */
250     int len = strlen(args[0]);
251     if (args[0][0== '!' && args[0][1== '#') {
252         
253         if (len == 2) {
254             /* !#  print all the history*/
255             PrintCommandHistory(counter);
256         }
257 
258         /* !#N print the last N commands */
259         if (len > 2) {
260             int n = GetNumFromString(args[0], 2, len - 1);
261             if (n == -1 || n > counter) {
262                 printf("Not valid\n");
263                 DeleteCurrentCommand();
264                 return;
265             }
266             PrintCommandHistory(n);
267         }
268 
269         return;
270     }
271 
272     /* !N 执行历史记录标号为N的指令 */
273     if(args[0][0== '!' && len > 1 && args[0][1!= '#') {
274         int n = GetNumFromString(args[0], 1, len - 1);
275         
276         if (n == -1 || n == 0 || n >= counter) {
277             printf("Not valid\n");
278             DeleteCurrentCommand();
279             return;
280         }
281         
282         DeleteCurrentCommand();
283         EchoArgs(commands_vec[n - 1]);
284         AddHistory(commands_vec[n - 1]);
285         ExcuteCommand(commands_vec[n - 1]);
286         return;
287     }
288     
289     /* non built in command*/
290     ExcuteNonBuiltinCommand(args);
291 }
292 
293 
294 /* MAIN PROCEDURE SECTION */
295 int main(int argc, char **argv)
296 {
297     Init();
298     while (1) {
299         char **args = GetInput();
300         ExcuteCommand(args);
301         args = NULL;
302     }
303     FreeComandsResouce();

304 /* end main() */ 

posted @ 2009-07-17 16:03  阁子  阅读(873)  评论(0编辑  收藏  举报