Shell实现1.0
今天的任务是实现Linux 下shell 界面的实现。
我们知道,Linux 下的外部命令是bash进程通过生成出子进程去调用bin/bash 下的相关命令来执行的。
我们根据这一特性,对stdin 端输入的字符获取并剖析。
每次获取字符串,先剖析字符串,将其中用于分割的空格都变成 '\0' 并且将字符串的内容拆分成多个字符串装入到一个包含多个字符串的数组中(我们设定数组最大成员个数为8)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <string.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <sys/wait.h> 8 #include <fcntl.h> 9 #include <ctype.h> 10 #include <myerr.h> 11 12 int main() 13 { 14 char buf[1024]; 15 16 while(1){ 17 memset(buf,0,sizeof(buf)); 18 printf("[ root@localhost file ]#"); 19 scanf("%[^\n]",buf); 20 getchar(); 21 if(strncmp(buf,"exit",4) == 0) 22 err_quit(NULL); 23 //read buf 24 else 25 do_parse(buf); 26 27 } 28 return 0; 29 }
第二步,定义字符串剖析函数。
将函数装入argv[8] 数组中,然后调用execvp 函数。
让我看一下execvp 函数的相关信息:
exec 的作用就是让当前进程直接跳转至其他函数,用一个新的进程(映像)替代现有的进程(映像)。
Linux下的字母都可以很好的做出引申并理解——
首先可以看到两个函数名中带 p 的函数,其意思为:函数会在PATH所指的目录下直接寻找相关命令。所以,如果函数名中不带有 p ,意为要输入的是绝对地址(相对地址)。
其次可以看到两个函数名中带有 e ,e 可以直译为 envp ----环境变量,也就是说,在跳转时要加上环境变量相关的参数。
最后可以看到一般函数是 v 一般函数是 l ,v 即 vector 可以理解成数组(浅),即传参以数组形式传,l 即 list 可以理解成链表(浅),即传参以多个独立的字符串传递。
1 void do_parse(char* buf) 2 { 3 int argc = 0; 4 char* argv[8]; 5 int IsNeedMoveFile = 0;//表示发现'>'的个数 6 int TheTransferFile = 0;//内容转移的位置。 7 8 //修改命令行。 9 //暂时只考虑一个>的情况,不考虑 >> 10 //这里需要改变argc IsNeedMoveFile TheTransferFile的值,所以要传地址 11 AlterBuf(buf,argv,&argc,&IsNeedMoveFile,&TheTransferFile); 12 13 //判断'>'的特殊情况 14 if(1 == Judge_Rediect(argv,argc,IsNeedMoveFile,TheTransferFile) ) 15 return; 16 17 //开始跑程序 18 Run_ThisProgress(argv ,argc ,IsNeedMoveFile ,TheTransferFile); 19 20 int i = 0; 21 for(i=0;i<argc;i++){ 22 printf("%s ",argv[i]); 23 } 24 printf("\n"); 25 }
在Myshell 实现1.0版本下,我加入了文件重定向符的判断和异常分析。
以下是字符串转换函数的源码:
1 void AlterBuf(char *buf,char *argv[],int* argc,int* IsNeedMF,int* TheFile)//需要改变,所以传指针 2 { 3 int i = 0; 4 int status = 0;//0--空格 1--字符 5 while(buf[i]!='\0') 6 { 7 // is '>' 8 if(buf[i] == '>' && isspace(buf[i+1]) && status == 0) 9 { 10 if (0 == i) 11 err_quit("write in error!\n"); 12 else{ 13 argv[(*argc)] = NULL; 14 (*argc)++; 15 *TheFile = *argc; 16 (*IsNeedMF) += 1; 17 } 18 } 19 //space->char 20 else if(!isspace(buf[i])&&status == 0){ 21 argv[(*argc)] = &buf[i]; 22 status = 1; 23 (*argc)++; 24 } 25 //is space 26 else if(isspace(buf[i])) 27 { 28 status = 0; 29 buf[i] ='\0'; 30 } 31 32 i++; 33 } 34 }
以下是文件重定向符 ‘>’ 的异常判断:
1 int Judge_Rediect(char *argv[],int argc,int IsNeedMF,int TheFile) 2 { 3 //没有标识符'>' 4 //no '>' 5 if(TheFile == 0 || IsNeedMF == 0) 6 argv[argc] = NULL; 7 8 //有多个'>'标识符 9 //have more than one '>' 10 else if(IsNeedMF > 1){ 11 err_msg("write in error!\n"); 12 return 1; 13 } 14 //有'>'却无文件 15 else if(IsNeedMF == 1 && TheFile == argc) 16 { 17 err_msg("The file can not be nameless!\n"); 18 return 1; 19 } 20 //不止一个文件 21 else if(IsNeedMF == 1 && (TheFile + 1) != argc) 22 { 23 int j = 0; 24 for(j = TheFile + 1;j <= argc;j++){ 25 err_msg("The file %s is needless!\n",argv[j]); 26 } 27 err_msg("Leave The Procedure!\n"); 28 return 1; 29 } 30 31 return 0; 32 }
处理完异常,最后是运行程序的代码:
1 //文件重定向实现 2 3 void Run_ThisProgress(char *argv[],int argc,int IsNeedMF,int TheFile) 4 { 5 if(argc != 0) 6 { 7 //让子进程去执行shell指令 8 if(fork() == 0){ 9 //有 '>' 文件重定向 10 if(IsNeedMF == 1) 11 { 12 close(1); 13 open(argv[TheFile],O_RDWR | O_CREAT,0644); 14 } 15 if(-1 == execvp(argv[0],argv)) 16 err_msg("Pid Exec failed!\n"); 17 } 18 19 wait(NULL); 20 } 21 }