簡易shell

該代碼實現了基本命令,重定向,多重管道的功能

  1 #include<stdio.h>
  2 #include<sys/stat.h>
  3 #include<sys/types.h>
  4 #include<fcntl.h>
  5 #include<unistd.h>
  6 #include<wait.h>
  7 #include<stdlib.h>
  8 #include<string.h>
  9 struct node{
 10     char *argv[1000];//存儲由管道分割的命令及其參數
 11     int in,out,file_num;//對於輸入輸出重定向以及是否有文件名進行標記
 12 };
 13 int split(char *s,struct node *comd)//對命令s進行解析
 14 {
 15     char *tmp=s,*args;
 16     int cnt=0,j=0;
 17     comd[cnt].argv[0] = strtok(tmp,"|");//對字符串tmp以“|”進行拆分,存放在comd數組中
 18     while(comd[cnt].argv[0] != NULL){
 19         cnt++;
 20         comd[cnt].argv[0] = strtok(NULL,"|");
 21         if(comd[cnt].argv[0] != NULL && cnt > 0 &&
 22                 !strcmp(comd[cnt].argv[0],comd[cnt-1].argv[0])){//對於管道間重複的命令進行處理
 23             cnt--;
 24         }
 25     }
 26     for(int i=0;i<=cnt;i++){//對於管道間每個命令進行處理,使得其能直接用於execvp
 27         j=0;
 28         int vis=0;
 29         args = comd[i].argv[0];
 30         comd[i].in = 0;
 31         comd[i].out=0;
 32         comd[i].file_num=-1;
 33         comd[i].argv[j] = strtok(args," ");//將字符串args以“ ”進行拆分使命令名和參數存放於argv數組中
 34         while(comd[i].argv[j] != NULL){
 35             j++;
 36             comd[i].argv[j] = strtok(NULL," ");
 37             if(comd[i].argv[j] != NULL && !strcmp(comd[i].argv[j],">")){//對於命令中的輸出重定向進行處理
 38                 comd[i].out = 1;
 39                 vis=1;
 40                 comd[i].file_num=j+1;
 41             }
 42             if(comd[i].argv[j] != NULL && !strcmp(comd[i].argv[j],"<")){//對命令中的輸入重定向進行處理
 43                 comd[i].in = 1;
 44                 vis=1;
 45                 comd[i].file_num=j+1;//標誌文件名稱的在數組中的位置
 46             }
 47         }
 48         if(vis)
 49             comd[i].argv[comd[i].file_num-1] = NULL;//在最後一個參數後加上NULL
 50     }
 51     return cnt;//返回命令個數
 52 }
 53 void put_out(struct node *comd,int n)//用於輸出驗證解析命令的正確性
 54 {
 55     int j=0;
 56     for(int i=0;i<n;i++){
 57         j=0;
 58         while(comd[i].argv[j] != NULL){
 59             if(j == 0){
 60                 printf("argv[0]: ");
 61             }
 62             if(j == 1){
 63                 printf(", args: ");
 64             }
 65             printf(" %s ",comd[i].argv[j]);
 66             j++;
 67         }
 68         if(comd[i].in){
 69             printf(",in(yes) file name: %s",comd[i].argv[comd[i].file_num]);
 70         }
 71         else printf(",in(NULL)");
 72         if(comd[i].out){
 73             printf(",out(yes) file name: %s \n",comd[i].argv[comd[i].file_num]);
 74         }
 75         else printf(",out(NULL)\n");
 76     }
 77 }
 78 int main(void)
 79 {
 80     while(1){//while循環用於一直接受用戶輸入命令
 81         int pipefd[20][2];//管道數組
 82         int fp;//文件描述符
 83         pid_t pid;
 84         for(int i=0;i<20;i++)
 85         {
 86             pipefd[i][0]=-1;
 87             pipefd[i][1]=-1;
 88         }
 89         if(STDOUT_FILENO != 1)//用dup2消除上一次的輸出重定向的影響
 90         {
 91             int duo_num = dup2(STDOUT_FILENO,1);
 92             if(duo_num == -1){
 93                 perror("duo2_out error: ");
 94                 exit(1);
 95             }
 96         }
 97         if(STDIN_FILENO != 0){//用dup2消除上一次的輸入重定向的影響
 98             int duo_num = dup2(STDIN_FILENO,0);
 99             if(duo_num == -1){
100                 perror("duo2_in error: ");
101                 exit(1);
102             }
103         }
104         struct node comd[100];
105         char s[100000];
106         printf("myshell_1$:");
107         fgets(s,100000,stdin);
108         int j=0;
109         while(s[j] != '\0'){//將末尾的'\n'處理掉
110             if(s[j] == '\n'){
111                 s[j]='\0';
112             }
113             j++;
114         }
115         int num=split(s,comd);//字符串解析
116         //put_out(comd,num);
117         if(num == 0){//處理空字符串
118             continue;
119         }
120         for(int i = 0; i < num;i++)//對管道命令創建管道
121         {
122             int error_num = pipe(pipefd[i]);
123             if(error_num == -1){
124                 perror("pipe error:");
125                 exit(1);
126             }
127         }
128         int i;
129         for(i=0;i < num;i++)//創建多個進程用來跑管道間的命令
130         {
131             pid = fork();
132             if(pid == -1)
133             {
134                 perror("fork error:");
135                 exit(1);
136             }
137             else if(pid == 0){
138                 break;
139             }
140         }
141         if(i == 0)//對於第一個命令會有輸入重定向,需特殊處理
142         {
143             for(int j = 1; j < num;j++)//關閉管道不用的讀端和寫端
144             {
145                 close(pipefd[j][0]);
146                 close(pipefd[j][1]);
147             }
148             if(comd[i].in){//輸入重定向的處理
149                 fp=open(comd[i].argv[comd[i].file_num],O_RDWR);
150                 if(fp == -1)
151                 {
152                     perror("open_in error:");
153                     exit(1);
154                 }
155                 if(dup2(fp,STDIN_FILENO) == -1){
156                     perror("dup2_重定向 error:");
157                     exit(1);
158                 }
159             }
160             if(comd[i].out)//輸出重定向的處理
161             {
162                 fp=open(comd[i].argv[comd[i].file_num],O_RDWR |
163                 O_CREAT,0644);
164                 if(fp == -1){
165                     perror("open out error:");
166                     exit(1);
167                 }
168                 if(dup2(fp,STDOUT_FILENO) == -1){
169                     perror("dup2_out_file error:");
170                     exit(1);
171                 }
172             }
173             if(close(pipefd[i][0]) == -1)//關閉讀端
174             {
175                 perror("close pipefd_0 error:");
176                 exit(1);
177             }
178             if(num > 1 && dup2(pipefd[i][1],STDOUT_FILENO) == -1){//若存在管道命令則將標準輸出重定向到管道的寫端
179                 perror("dup2_pipe_0 error:");
180                 exit(1);
181             }
182             if(execvp(comd[i].argv[0],comd[i].argv) == -1){//用execvp替換進程的任務
183                 perror("execvp_0 error:");
184                 exit(1);
185             }
186         }
187         else if(i == num-1){//對於最後一個命令會存在輸出重定向,需特殊處理
188             for(int j = 0; j < num;j++)//將不使用的管道讀寫端關閉
189             {
190                 if(j == i-1)
191                     continue;
192                 close(pipefd[j][0]);
193                 close(pipefd[j][1]);
194             }
195             if(comd[i].out)//處理輸出重定向
196             {
197                 fp=open(comd[i].argv[comd[i].file_num],O_RDWR |
198                 O_CREAT,0644);
199                 if(fp == -1){
200                     perror("open out error:");
201                     exit(1);
202                 }
203                 if(dup2(fp,STDOUT_FILENO) == -1){
204                     perror("dup2_out_file error:");
205                     exit(1);
206                 }
207             }
208             if(close(pipefd[i-1][1]) == -1){//關閉管道寫端
209                 perror("close pipefd_num-1 error:");
210                 exit(1);
211             }
212             if(dup2(pipefd[i-1][0],STDIN_FILENO) == -1){//將標準輸入重定向到管道的讀端
213                 perror("dup2_pipe_num-1 error:");
214                 exit(1);
215             }
216             if(execvp(comd[i].argv[0],comd[i].argv) == -1)//用execvp函數替換子進程的任務
217             {
218                 perror("execvp_num-1 error:");
219                 exit(1);
220             }
221         }
222         else if(i>0 && i < num-1){//對於第一個和最後一個之間的命令無重定向,統一進行處理
223             for(int j = 0; j < num;j++)//關閉不使用的管道的讀端和寫端
224             {
225                 if(j == i || j == i-1)
226                     continue;
227                 close(pipefd[j][0]);
228                 close(pipefd[j][1]);
229             }
230             if(close(pipefd[i-1][1]) == -1){//關閉前一個管道的寫端
231                 perror("close pipefd_else_i-1 error:");
232                 exit(1);
233             }
234             if(close(pipefd[i][0]) == -1){//關閉當前管道的讀端
235                 perror("close pipefd_else_i error:");
236                 exit(1);
237             }
238             if(dup2(pipefd[i-1][0],STDIN_FILENO) == -1)//將標準輸入重定向到前一個管道的讀端
239             {
240                 perror("dup2_in error:");
241                 exit(1);
242             }
243             if(dup2(pipefd[i][1],STDOUT_FILENO) == -1)//將標準輸出重定向到前一個管道的寫端
244             {
245                 perror("dup2_out error:");
246                 exit(1);
247             }
248             if(execvp(comd[i].argv[0],comd[i].argv) == -1)//用execvp替換子進程的任務
249             {
250                 perror("execv error:");
251                 exit(1);
252             }
253         }
254         int set;
255         if(pid > 0)
256         {
257             for(int i = 0; i < num;i++)//關閉父進程管道的讀端寫端
258             {
259                 close(pipefd[i][0]);
260                 close(pipefd[i][1]);
261             }
262             do{
263                 set = waitpid(-1,NULL,WNOHANG);
264             }while(set == 0);//用輪詢的方式回收子進程
265         }
266     }
267     return 0;
268 }     
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章