該代碼實現了基本命令,重定向,多重管道的功能
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 }