简单c语言实现unix shell【转载】

来源:互联网 发布:nfc世界网络银行商城 编辑:程序博客网 时间:2024/05/16 15:51
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/wait.h>#define SHELL_CD 1#define SHELL_FORK 2#define SHELL_EMPTY 3#define BUFSIZE 1024#define CMDNUMBER 100int read_char(char *str);int parse_args(char *args[], char *arg);int isquit(char *args);char *shell_prompt(char *promptbuf);int dealarg(char *arg);int shell_cd(char *args[]);int shell_fork(char *args[]);int get_cmdnum(char *cmd);int mutifork(char *args[]);int main(int argc, char *argv[]){char arg[BUFSIZE];ssize_t readbytes;char str_prompt[BUFSIZE + 1];while(1){fprintf(stdout, "%s", shell_prompt(str_prompt));fflush(stdout);memset(arg, 0, sizeof(arg));if((readbytes = read(STDIN_FILENO, arg, BUFSIZE)) < 0){fprintf(stderr, "read failed: %s\n", strerror(errno));exit(1);}if(arg[readbytes - 1] == '\n'){ /* clear '\n' at end of arg */arg[readbytes - 1] = '\0';}if(isquit(arg) == 1){ /* check quit or not */break;}dealarg(arg);}return 0;}int parse_args(char *args[], char *arg)  //done!divide command by space or tab or new line{char **p;char *q;int ch;int count;p = args;q = arg;count = 0;while(*q != '\0'){while((ch = read_char(q)) == ' '){ /* skip space */q++;}*p++ = q++;count++;ch = read_char(q);while(ch != ' ' && ch != '\0'){ /* find first space after word */q++;ch = read_char(q);}if(ch != '\0'){*q++ = '\0';ch = read_char(q);}}return count;}int read_char(char *str)  //done  if char str point to equals space tab or newline character return space.{char filter[] = " \t\n";char *p;int flag;flag = 0;p = filter;while(*p != '\0'){if(*str == *p){flag = 1;break;}p++;}if(flag == 1){return ' ';}else{return *str;}}char *shell_prompt(char *promptbuf)   //done, use the current path as prompt{char tmpbuf[BUFSIZE + 1];memset(promptbuf, 0, BUFSIZE + 1);memset(tmpbuf, 0, sizeof(tmpbuf));if(getcwd(tmpbuf, sizeof(tmpbuf) - 1) == NULL){fprintf(stderr, "%s:%d: getcwd failed: %s\n",__FILE__, __LINE__, strerror(errno));exit(1);}snprintf(promptbuf, BUFSIZE, "%s$ ", tmpbuf);return promptbuf;}int isquit(char *arg)      //done{if(strcmp(arg, "exit") == 0 || strcmp(arg, "EXIT") == 0){return 1;}else{return 0;}}int dealarg(char *arg)      //done   first parse and then deal{int cmdnum;char *args[CMDNUMBER];int argnum;argnum = parse_args(args, arg);args[argnum] = NULL;cmdnum = get_cmdnum(args[0]);switch(cmdnum){case SHELL_EMPTY:break;case SHELL_CD:shell_cd(args);break;case SHELL_FORK:shell_fork(args);break;default:fprintf(stdout, "%s:%d: getcmd failed\n", __FILE__, __LINE__);break;}return 0;}int shell_fork(char *args[])    //done{pid_t pid[CMDNUMBER];int status;int fork_num;char **p;char *q;fork_num = 1;p = args;while(*p != NULL){q = *p;while(*q != '\0'){if(*q == '|'){fork_num++;}q++;}p++;}if(fork_num < 2){if((pid[0] = fork()) < 0){perror("fork");exit(1);}else if(pid[0] == 0){if(execvp(args[0], args) < 0){perror("");exit(1);}exit(0);}}if(fork_num < 2){waitpid(pid[0], &status, 0);}else{status = mutifork(args);}return status;}int shell_cd(char *args[])      //done{char buf[BUFSIZE + 1];memset(buf, 0, BUFSIZE + 1);if(args[1][0] != '/' && args[1][0] != '.'){if(getcwd(buf, BUFSIZE) == NULL){fprintf(stderr, "%s:%d: getcwd failed: %s\n", __FILE__,__LINE__, strerror(errno));return -1;}strncat(buf, "/", BUFSIZE - strlen(buf));}strncat(buf, args[1], BUFSIZE - strlen(buf));if(chdir(buf) == -1){fprintf(stderr, "%s:%d: chdir failed: %s\n", __FILE__,__LINE__, strerror(errno));}return 0;}int get_cmdnum(char *cmd)      //done,  empty command, cd command and normal shell command{if(cmd == NULL) return SHELL_EMPTY;if(strcmp(cmd, "cd") == 0) return SHELL_CD;return SHELL_FORK;}int mutifork(char *args[])    //done{int pipefd[CMDNUMBER][2];pid_t pid[CMDNUMBER];int i, j;int count;int status;char **arg_child[CMDNUMBER];char **p;char ***q;count = 0;p = args; q = arg_child;while(*p != NULL && p != NULL){*q++ = p;count++;while(*p != NULL && strcmp(*p, "|") != 0){p++;}if(*p != NULL){*p++ = NULL;}}*q = NULL;for(i = 0; i < count; i++){if(pipe(pipefd[i]) < 0){perror("pipe");return -1;}if((pid[i] = fork()) < 0){fprintf(stderr, "%s:%d: fork() failed: %s\n", __FILE__,__LINE__, strerror(errno));return -1;}else if(pid[i] == 0){if(i == 0){ /* the first child */close(pipefd[i][0]); /* close curr process read */if(dup2(pipefd[i][1], STDOUT_FILENO) < 0){perror("dup2 failed");return -1;}}else if(i == count - 1){ /* the last child */for(j = 0; j < i - 1; j++){ /* close unuse pipefd */close(pipefd[j][1]);close(pipefd[j][0]);}close(pipefd[j][1]); /* close prev process end of write */close(pipefd[i][0]); /* close curr process end of read */if(dup2(pipefd[j][0], STDIN_FILENO) < 0){perror("dup2 failed");return -1;}}else{for(j = 0; j < i - 1; j++){ /* close unuse pipefd */close(pipefd[j][1]);close(pipefd[j][0]);}close(pipefd[j][1]); /* close prev process end of write */close(pipefd[i][0]); /* close curr process end of read */if(dup2(pipefd[j][0], STDIN_FILENO) < 0){perror("dup2 failed");return -1;}if(dup2(pipefd[i][1], STDOUT_FILENO) < 0){perror("dup2 failed");return -1;}}if(execvp(arg_child[i][0], arg_child[i]) < 0){fprintf(stderr, "%s:%d: fork() failed: %s\n", __FILE__,__LINE__, strerror(errno));exit(1);}exit(0);}}for(i = 0; i < count; i++){/* close all pipe file descriptor */close(pipefd[i][0]);close(pipefd[i][1]);}for(i = 0; i < count; i++){//pid_t waitpid(pid_t pid, int *status, int options);waitpid(pid[i], &status, 0);if(status != 0){return status;}}return status;}