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 }