C语言 实现一个简单的Shell (支持管道和"cd")

来源:互联网 发布:mac date 修改时间 编辑:程序博客网 时间:2024/06/06 06:11

废话不说,直接上代码


shell.h

/******************************************************** * @author  Airead Fan <fgh1987168@gmail.com>* * @date    2011 9月 08 12:44:18 CST* ******************************************************** *after studying C 52 days* *after studying APUE 17 days* ********************************************************/#ifndef SHELL_H#define SHELL_H#define SHELL_CD 1#define SHELL_FORK 2#define SHELL_EMPTY 3int 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[]);#endif


shell.c

/******************************************************** * @author  Airead Fan <fgh1987168@gmail.com>* * @date    2011 9月 09 20:00:47 CST* ******************************************************** *after studying C 53 days* *after studying APUE 18 days* ********************************************************//* * This program implement shell */#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>#include "shell.h"#define DEBUG 1#define BUFSIZE 1024#define CMDNUMBER 100int 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);//void *memset(void *s, int c, size_t n);memset(arg, 0, sizeof(arg));//ssize_t read(int fd, void *buf, size_t count);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;}//int dealarg(char *arg);dealarg(arg);}return 0;}/* * parse args * note that: args[n] and arg shared memory! */int parse_args(char *args[], char *arg){char **p;char *q;int ch;int count;p = args;q = arg;count = 0;while(*q != '\0'){//int read_char(char *arg);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;}/* * filter string */int read_char(char *str){char filter[] = " \t\n";char *p;int flag;/* flag 1 return ' ', 0 return *str */flag = 0;p = filter;while(*p != '\0'){if(*str == *p){flag = 1;break;}p++;}if(flag == 1){return ' ';}else{return *str;}}/* * print shell prompt */char *shell_prompt(char *promptbuf){char tmpbuf[BUFSIZE + 1];//void *memset(void *s, int c, size_t n);memset(promptbuf, 0, BUFSIZE + 1);memset(tmpbuf, 0, sizeof(tmpbuf));//char *getcwd(char *buf, size_t size);if(getcwd(tmpbuf, sizeof(tmpbuf) - 1) == NULL){fprintf(stderr, "%s:%d: getcwd failed: %s\n", __FILE__, __LINE__, strerror(errno));exit(1);}//int snprintf(char *str, size_t size, const char *format, ...);snprintf(promptbuf, BUFSIZE, "%s$ ", tmpbuf);return promptbuf;}/* * if args == quit */int isquit(char *arg){//int strcmp(const char *s1, const char *s2);if(strcmp(arg, "quit") == 0){return 1;}else{return 0;}}/* * Receive message and deal something */int dealarg(char *arg){int cmdnum;char *args[CMDNUMBER];int argnum;//int parse_args(char *args[], char *arg); argnum = parse_args(args, arg);args[argnum] = NULL;#if DEBUGint i;fprintf(stdout, "argnum = %d\n", argnum);for(i = 0; i < argnum; i++){fprintf(stdout, "[%d]%s\n", i, args[i]);}#endif//int get_cmdnum(char *cmd)cmdnum = get_cmdnum(args[0]);switch(cmdnum){case SHELL_EMPTY:break;case SHELL_CD://int shell_cd(char *args[])shell_cd(args);break;case SHELL_FORK://int shell_fork(char *args[])shell_fork(args);break;default:fprintf(stdout, "%s:%d: getcmd failed\n", __FILE__, __LINE__);break;}return 0;}/* * fork and called exec */int shell_fork(char *args[]){pid_t pid[CMDNUMBER];int status;int fork_num;char **p;/* point args */char *q;/* point *args *//* get numbers of child process*/fork_num = 1;p = args;while(*p != NULL){q = *p;while(*q != '\0'){if(*q == '|'){fork_num++;}q++;}p++;}#if DEBUGfprintf(stdout, "fork_num = %d\n", fork_num);#endif/* case: child process number is one */if(fork_num < 2){//pid_t fork(void);if((pid[0] = fork()) < 0){/* error */perror("fork");exit(1);}else if(pid[0] == 0){/* child process *///int execvp(const char *file, char *const argv[]);if(execvp(args[0], args) < 0){perror("");exit(1);}exit(0);}}/* parent process *///pid_t waitpid(pid_t pid, int *status, int options);if(fork_num < 2){waitpid(pid[0], &status, 0);}else{//int mutifork(char *args[])status = mutifork(args);}return status;}/* * likes shell's cd */int shell_cd(char *args[]){char buf[BUFSIZE + 1];memset(buf, 0, BUFSIZE + 1);if(args[1][0] != '/' && args[1][0] != '.'){//char *getcwd(char *buf, size_t size);if(getcwd(buf, BUFSIZE) == NULL){fprintf(stderr, "%s:%d: getcwd failed: %s\n", __FILE__, __LINE__, strerror(errno));return -1;}strncat(buf, "/", BUFSIZE - strlen(buf));}//char *strncat(char *dest, const char *src, size_t n);strncat(buf, args[1], BUFSIZE - strlen(buf));#if DEBUGfprintf(stdout, "%s\n", buf);#endif//int chdir(const char *path);if(chdir(buf) == -1){fprintf(stderr, "%s:%d: chdir failed: %s\n", __FILE__, __LINE__, strerror(errno));}return 0;}/* * Change cmd to int, according to shell.h */int get_cmdnum(char *cmd){if(cmd == NULL) return SHELL_EMPTY;if(strcmp(cmd, "cd") == 0) return SHELL_CD;return SHELL_FORK;}int mutifork(char *args[]){int pipefd[CMDNUMBER][2];pid_t pid[CMDNUMBER];int i, j;int count;int status;char **arg_child[CMDNUMBER];char **p;char ***q;/* parse and split args to child arg */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;#if DEBUG/* check child args */fprintf(stdout, "----------------------------------------\n");fprintf(stdout, "count = %d\n", count);q = arg_child; i = 0;while(*q != NULL){p = *q++;while(*p != NULL){fprintf(stdout, "[%d]%s\n", i, *p++);}i++;}#endif/* fork count child process */for(i = 0; i < count; i++){/* init pipe file descriptor */if(pipe(pipefd[i]) < 0){ /* FIXME: excess one */perror("pipe");return -1;}/* fork child i */if((pid[i] = fork()) < 0){fprintf(stderr, "%s:%d: fork() failed: %s\n", __FILE__,__LINE__, strerror(errno));return -1;}else if(pid[i] == 0){/* child i *///int dup2(int oldfd, int newfd);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;}}//int execvp(const char *file, char *const argv[]);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);/* child process exit */}}/* parent process */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;}