这是《linux c编程一站式学习》中的一道练习题
用讲过的各种C 函数实现一个简单的交互式Shell,要求:
1 、给出提示符,让用户输入一行命令,识别程序名和参数并调用适当的exec函数执行程序,待执
行完成后再次给出提示符。
2 、识别和处理以下符号:
简单的标准输入输出重定向(< 和> ):仿照例 30.5 “wrapper” ,先dup2然后exec。
管道(| ):Shell进程先调用pipe创建一对管道描述符,然后fork出两个子进程,一个子进程
关闭读端,调用dup2把写端赋给标准输出,另一个子进程关闭写端,调用dup2把读端赋给标
准输入,两个子进程分别调用exec执行程序,而Shell进程把管道的两端都关闭,调用wait等
待两个子进程终止。
你的程序应该可以处理以下命令:
○ls△-l △-R○>○file1○
○cat○○file1○
○ 表示零个或多个空格,△表示一个或多个空格
下面是我的思路:
1:先对一条命令进行分析,看它有哪些管道(有那些程序需要执行),有哪些重定向,
2:然后打开重定向文件,再最后实现管道,执行程序
3:如果有多个程序需要执行,重复第二部
对于一条命令如:cat < file1|wc -c > file1
管道“|”将其分为两部分,cat < file1和wc -c > file1,所以首先根据“|”用把它分成两部分(split1()函数实现这一功能),再分别调用split2()函数根据重定向符探测各自的重定向文件,且前一部分的标准输出是后一部分的标准输入(这是管道的实现,我把它放到第3步再实现)。
在split2里,处理完“< file1”这种符号后,我把“< file1”替换成“ ”,也就是空格,这样一来,就方便根据空格来判别参数了,比如“wc -c > file1”的参数是“wc”和“-c”。
我用:
struct {
char* cmd;//用以记录用户输入的命令
char* prog;//需要执行的程序名
char* arg[10];//参数,最多10个
char infile[32];//最长32,用以记录重定向文件
char outfile[32];
}info[10];
来记录需要执行的程序名(最多10个),以及参数、重定向文件。
还用了int myfd[9][2]记录管道。
管道的实现:
假如已经执行了两次循环,已经运行了info[0].prog、info[1].prog。正准备执行info[2].prog,
此时info[1].prog和info[2].prog之间已经有管道了(上一个循环)。管道的两个文件描述符保存在myfd[1][0]和myfd[1][1]中。
检测测info[3].prog是否为空,否的话则新建一个管道myfd[2],用于info[2].prog和info[3].prog之间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> //系统类型 #include <sys/wait.h> //waitpid #include <fcntl.h> //dup2 struct { char* cmd;//用以记录用户输入的命令 char* prog;//需要执行的程序名 char* arg[10];//参数,最多10个 char infile[32];//最长32,用以记录重定向文件 char outfile[32]; }info[10]; char input[512];//用户输入 int myfd[9][2];//10个程序,9个管道 static void split1();//第一次分割,检测是否有"|" static void split2(int n);//第二次分割,检测是否有“”以及" " static void myrun(); int main(int argc, char *argv[]) { int i; mystart: for (i=0; i bzero(&info[i],sizeof(info[i])); printf("myshell:"); gets(input); split1(); myrun(); goto mystart; return 0; } void split1() { char* str; int i; for (i=0,str=input; ; i++,str=NULL){ info[i].cmd=strtok(str,"|"); if (info[i].cmd==NULL) break; split2(i);//若redirect是调用非线程安全的strtok的话,会覆盖mypipe的数据 } } void split2(int n) { /* //这个算法处理不了cat int i; char* pretoken; char* token; char* saveptr; info[n].prog=strtok_r(info[n].cmd," ",&saveptr); for (i=0; ; i++){ token=strtok_r(NULL," ",&saveptr); if (token==NULL) break; if (strncmp("1) info[n].infile=token+1; } else if (strncmp(">",token,1)==0){ if (strlen(token)>1) info[n].outfile=token+1; } else if (strcmp(" info[n].outfile=token; } else { info[n].arg[i]=token; } pretoken=token; } */ char *saveptr,*pos,*tmp1,*tmp2,*tmp3; int i; if ( (tmp1=strstr(info[n].cmd," pos=tmp1; tmp1++; if (strncmp(tmp1," ",1) == 0) tmp1++; tmp2=strstr(tmp1," ");//file2的情况 tmp3=strstr(tmp1,">");//file2的情况 if (tmp2==NULL && tmp3==NULL){//这么多条件同时只能有一个成立,所以是else if memcpy(info[n].infile,tmp1,strlen(tmp1)); memset(pos,' ',strlen(pos)); } else if (tmp2 == NULL){ memcpy(info[n].infile,tmp1,tmp3-tmp1); memset(pos,' ',tmp3-pos); } else if (tmp3 == NULL){ memcpy(info[n].infile,tmp1,tmp2-tmp1); memset(pos,' ',tmp2-pos); } else if (tmp3< tmp2){ memcpy(info[n].infile,tmp1,tmp3-tmp1); memset(pos,' ',tmp3-pos); } else { memcpy(info[n].infile,tmp1,tmp2-tmp1); memset(pos,' ',tmp2-pos); } } if ( (tmp1=strstr(info[n].cmd,">")) != NULL){ pos=tmp1; tmp1++; if (strncmp(tmp1," ",1) == 0) tmp1++; tmp2=strstr(tmp1," "); tmp3=strstr(tmp1," if (tmp2==NULL && tmp3==NULL){ memcpy(info[n].outfile,tmp1,strlen(tmp1)); memset(pos,' ',strlen(pos)); } else if (tmp2 == NULL){ memcpy(info[n].outfile,tmp1,tmp3-tmp1); memset(pos,' ',tmp3-pos); } else if (tmp3 == NULL){ memcpy(info[n].outfile,tmp1,tmp2-tmp1); memset(pos,' ',tmp2-pos); } else if (tmp3< tmp2){ memcpy(info[n].outfile,tmp1,tmp3-tmp1); memset(pos,' ',tmp3-pos); } else { memcpy(info[n].outfile,tmp1,tmp2-tmp1); memset(pos,' ',tmp2-pos); } // info[n].outfile = (char*)malloc(tmp2-tmp1+1); /*bzero(info[n].outfile,32); tmp2 ? memcpy(info[n].outfile,tmp1,tmp2-tmp1) : memcpy(info[n].outfile,tmp1,strlen(tmp1)); tmp2 ? memset(pos,' ',tmp2-pos) : memset(pos,' ',strlen(pos));//把 } info[n].arg[0]=info[n].prog=strtok_r(info[n].cmd," ",&saveptr);//第一个是程序名 for (i=1; ;i++){//检测参数 info[n].arg[i]=strtok_r(NULL," ",&saveptr); if (info[n].arg[i] == NULL) break; } } void myrun() { int i, status, fd; pid_t pid; for (i=0; info[i].prog!=NULL; i++){ if (info[i+1].prog != NULL){//后面有程序,则创建管道myfd[i][] if (pipe(&myfd[i]) < 0) //也可以是pipe(myfd[i]),数组做优质变成指针 perror("pipe"); } if ( (pid=fork()) < 0) perror("fork"); else if (pid == 0){ if (myfd[i][0] != 0){//本进程和后一进程之间的管道读端 close(myfd[i][0]);//关闭读端 dup2(myfd[i][1],STDOUT_FILENO); } if (i > 0) if (myfd[i-1][0] != 0){//本进程与前一进程之间的管道读端 close(myfd[i-1][1]);//关闭写端 dup2(myfd[i-1][0],STDIN_FILENO); } if (strlen(info[i].infile) != 0){ //不能是 if (info[i].infile != NULL),因为infile不是指针 if ( (fd = open(info[i].infile,O_RDONLY)) < 0){ perror("open"); exit(-1); } dup2(fd,STDIN_FILENO); close(fd); } if (strlen(info[i].outfile) != 0){ if ( (fd = open(info[i].outfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR)) < 0){ perror("open"); exit(-1); } dup2(fd,STDOUT_FILENO); close(fd); } execvp(info[i].prog, info[i].arg); perror("exec"); } else //wait(&status);//不可以在这等待子程序终止,应该在for结束之后 if (myfd[i][0] != 0){//本进程和后一进程之间的管道读端 //close(myfd[i][0]);//如果父进程关闭读端,读端引用变为0,因为第二个子进程还没产生,这样的话导致往写端write的进程终止 close(myfd[i][1]); } } //waitpid(pid, &status, 0);这个会阻塞 wait(&status); } |
发表评论
要发表评论,您必须先登录。