Linux C small project - implement your own myshell

A simple shell with functions: interpreting and executing commands, supporting input and output redirection, supporting pipelines, and running programs in the background

Supported command formats:

       Single command: such as ls

       Commands with 1 or more parameters: such as ls -l /tmp

       Commands with an output redirection or input redirection or pipe: e.g. ls -l > a, wc -c < a, ls -l / | wc -c

       The background operator & can be added at the end of each of the above commands, such as ls &

        Type exit or logout to exit myshell


  1 #include <stdio.h>
  2 #include <stdlib.h>     // exit
  3 #include <string.h>
  4 #include <fcntl.h>      // O_RDWR
  5 #include <dirent.h> // DIR, struct dirent
  6
  7 #define BUFFSIZE 64
  8
  9 #define normal 0 // normal command
 10 #define out_redirect 1 // output redirection
 11 #define in_redirect 2 // input redirection
 12 #define have_pipe 3 // There is a pipe in the command
 13
 14 void print_prompt(); // print prompt
 15 void get_input(char *); // read the command entered by the user
 16 // A two-dimensional array is used as a function parameter, the length of the first dimension can not be specified, but the length of the second dimension must be specified
 17 // You can also use a pointer to a one-dimensional array with 8 elements, char (*a)[8]
 18 void explain_input(char *, int *, char (*a)[8]); // Parse the input command
 19 void do_cmd(int, char a[][8]); // execute command
 20 int find_command(char *); // find the executable program in the command
 21
 22 int main(int argc, char *argv[])
 23 {
 24         char buf[BUFFSIZE];
 25 char fraudulent[10][8];
 26         int argcount;
 27
 28         while(1)
 29         {
 30                 memset(buf, 0, BUFFSIZE);
 31 print_prompt(); // print prompt
 32 get_input(buf); // read user input
 33                 if(strcmp(buf, "exit")==0 || strcmp(buf, "logout")==0)
 34                         break;
 35                 int i;
 36 for(i=0; i<10; i++) // initialize the array holding the command and its parameters
 37 arglist [i] [0] = '\ 0';
 38 argcount = 0; // count, the number of words in the command
 39 explain_input(buf, &argcount, arglist); // parse command
 40 do_cmd(argcount, arglist); // execute command
 41         }
 42         return 0;
 43 }
 44
 45 void print_prompt()
 46 {
 47         printf("myshell$$ ");
 48 // fflush(stdout); // fflush flushes stdout
 49 }
 50
 51 void get_input(char *buf)
 52 {
 53         fgets(buf, BUFFSIZE, stdin);    // fgets
 54 int len ​​= strlen(buf); // newline character \n is counted, terminator character is not counted \0
 55         if(len >= BUFFSIZE)
 56         {
 57                 printf("ERROR: command is too long !\n");
 58                 exit(-1);
 59         }
 60 buf[len-1] = '\0'; // remove newline characters read
 61 }
 62
 63 // Parse the command in buf, the result is stored in arglist, the number of commands and their parameters is argcount
 64 // For example, "ls -l" command, then arglist[0], arglist[1] are ls, -l respectively
 65 void explain_input(char *buf, int *argcount, char arglist[][8])
 66 {
 67         char *p = buf;
 68
 69 // Split the entire string entered by the user into words
 70 // Store in each row of the 2D array
 71         while(*p != '\0')
 72         {
 73                 if(*p == ' ')
 74                         p++;
 75                 else
 76                 {
 77                         char *q = p;
 78 int len ​​= 0; // word length
 79                         while((*q!=' ') && (*q!='\0'))
 80                         {
 81 q ++; len ++;
 82                         }
 83 // Store the currently disassembled word in a row in a two-dimensional array
 84 strncpy (arglist [* argcount], p, len + 1);
 85 arglist [* argcount] [len] = '\ 0';
 86                         (*argcount)++;
 87 p = q;
 88                 }
 89         }
 90 }
 91
 92 //
 93 void do_cmd(int argcount, char arglist[10][8])
 94 {
 95 // Array of pointers, each element points to a row in a two-dimensional array
 96 // arg stores all commands and their parameters, argnext stores the command after the pipe character
 97         char *arg[argcount+1], *argnext[argcount+1];
 98         int i, flag = 0, how = 0, background = 0;
 99         char *file;
100         pid_t pid;
101
102 // Extract command
103         for(i=0; i<argcount; i++)
104 arg [i] = arglist [i];
105         arg[argcount] = NULL;
106
107 // Check if the command line has a background runner
108         for(i=0; i<argcount; i++)
109         {
110                 if(strncmp(arg[i], "&", 1) == 0)        // strncmp
111 { // The background runner must be at the end of the command, otherwise the command format is wrong
112                         if(i == argcount-1)
113                         {
114                                 background = 1;
115                                 arg[argcount-1] = NULL;
116                                 break;
117                         }
118                         else
119                         {
120                                 printf("ERROR: wrong command about backgrount\n");
121                                 return;
122                         }
123                 }
124         }
125
126         for(i=0; arg[i]!=NULL; i++)
127         {
128                 if(strcmp(arg[i], ">") == 0)
129                 {
130                         flag++;
131                         how = out_redirect;
132 if(arg[i+1] == NULL) // output redirector at the end
133 flag++; // Make flag greater than 1 to inform the command format error
134                 }
135                 if(strcmp(arg[i], "<") == 0)
136                 {
137                         flag++;
138                         how = in_redirect;
139 if(i == 0) // input redirector is at the top
140                                 flag++;
141                 }
142                 if(strcmp(arg[i], "|") == 0)
143                 {
144                         flag++;
145                         how = have_pipe;
146 if(arg[i+1] == NULL) // pipe last
147                                 flag++;
148 if(i == 0) // pipe first
149                                 flag++;
150                 }
151         }
152 // flag is greater than 1, indicating that it contains two or more of >,<,| at the same time, this program does not support
153 // or command format error
154         if(flag > 1)
155         {
156                 printf("ERROR: wrong command about >,<,|\n");
157                 return;
158         }
159
160 if(how == out_redirect) // command contains only one output redirector
161         {
162                 for(i=0; arg[i]!=NULL; i++)
163                         if(strcmp(arg[i], ">") == 0)
164                         {
165 file = arg[i+1]; // Get the file name of the output redirection
166                                 arg[i] = NULL;
167                         }
168         }
169
170 if(how == in_redirect) // command contains only one input redirector
171         {
172                 for(i=0; arg[i] != NULL; i++)
173                         if(strcmp(arg[i], "<") == 0)
174                         {
175                                 file = arg[i+1];
176                                 arg[i] = NULL;
177                         }
178         }
179
180 if(how == have_pipe) // command contains only one pipe symbol
181         {
182                 for(i=0; arg[i]!=NULL; i++)
183                         if(strcmp(arg[i], "|") == 0)
184                         {
185                                 arg[i] = NULL;
186                                 i++;
187                                 int j = 0;
188 // Store the command following the pipe character in argnext
189                                 while(arg[i] != NULL)
190                                 {
191                                         argnext[j++] = arg[i++];
192                                 }
193                                 argnext[j] = NULL;
194                                 break;
195                         }
196         }
197
198 pid = fork(); // create child process
199         if(pid < 0)
200         {
201                 perror("fork failure");
202                 return;
203         }
204
205         switch(how)
206         {
207 case 0: // general command
208 if(pid==0) // The child process executes the command entered by the user
209                         {
210 if(!find_command(arg[0])) // Determine whether the command is executable
211                                 {
212                                         printf("%s: command not found\n", arg[0]);
213                                         exit(0);
214                                 }
215 execvp(arg[0], arg); // execvp starts executing the command
216                                 exit(0);
217                         }
218                         break;
219 case 1: // command contains output redirector
220                         if(pid == 0)
221                         {
222                                 if(!find_command(arg[0]))
223                                 {
224                                         printf("%s: command not found\n", arg[0]);
225                                         exit(0);
226                                 }
227 // Open or create a new output redirected file
228                                 int fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0644);
229 // Copy standard output to open file descriptor, i.e. replace standard output with file descriptor
230                                 dup2(fd, 1);    // dup2(int oldfd, int newfd)
231 execvp (arg [0], arg); // execvp
232                                 exit(0);
233                         }
234                         break;
235 case 2: // command contains input redirector
236                         if(pid == 0)
237                         {
238                                 if(!find_command(arg[0]))
239                                 {
240                                         printf("%s: command not found\n", arg[0]);
241                                         exit(0);
242                                 }
243                                 int fd = open(file, O_RDONLY);
244                                 dup2(fd, 0);
245 execvp (arg [0], arg);
246                                 exit(0);
247                         }
248                         break;
249 case 3: // command contains pipe character
250 if(pid == 0) // child process
251                         {
252                                 pid_t pid2;
253                                 int fd2;
254 if((pid2=fork()) < 0) // A new child process is being created in the current child process
255                                 {
256                                         perror("fork2 failure");
257                                         return;
258                                 }
259 if(pid2 == 0) // The newly created child process executes the command before the pipe character
260                                 {
261                                         if(!find_command(arg[0]))
262                                         {
263                                                 printf("%s: command not found\n", arg[0]);
264                                                 exit(0);
265                                         }
266 // Store the execution result of the command before the pipe character in fd2
267                                         fd2 = open("/tmp/youdontknowfile",
268                                                         O_WRONLY | O_CREAT | O_TRUNC, 0644);
269 ​​dup2(fd2, 1); // redirect stdout
270 execvp (arg [0], arg);
271                                         exit(0);
272                                 }
273 waitpid(pid2, NULL, 0); // wait for the command before the pipe character to return
274                                 if(!find_command(argnext[0]))
275                                 {
276                                         printf("%s: command not found\n", argnext[0]);
277                                         exit(0);
278                                 }
279                                 fd2 = open("/tmp/youdontknowfile", O_RDONLY);
280 dup2(fd2, 0); // define fd2 as standard input
281 execvp(argnext[0], argnext); // execute the command after the pipe character
282                                 exit(0);
283                         }
284                         //remove("/tmp/youdontknowfile");
285                         //unlink("/tmp/youdontknowfile");
286                         break;
287                 default:
288                         break;
289         }
290 // If there is a background running character in the command, the parent process returns directly without waiting for the child process to return
291         if(background == 1)
292         {
293                 printf("[process id %d]\n", pid);
294                 return;
295         }
296 waitpid(pid, NULL, 0); // waitpid parent process waits for child process to return
297 }
298
299 // Determine whether the command is executable and whether there is a corresponding executable file
300 int find_command(char *command)
301 {
302 DIR *dir;
303 struct dirent * ptr;
304         char *path[] = {"./", "/bin", "/usr/bin", NULL};
305 // When the command "./build" is entered, match the build command with the build file in the directory
306         if(strncmp(command, "./", 2) == 0)
307                 command = command + 2;
308         int i = 0;
309         while(path[i] != NULL)
310         {
311 if((dir=opendir(path[i])) == NULL) // open directory
312                         printf("cannot open /bin\n");
313 while((ptr=readdir(dir)) != NULL) // read list of files in directory
314                         if(strcmp(ptr->d_name, command) == 0)
315                         {
316                                 closedir(dir);
317                                 return 1;
318                         }
319                 closedir(dir);
320                 i++;
321         }
322         return 0;
323 }

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326700229&siteId=291194637