参考代码:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> // Simplifed xv6 shell. #define MAXARGS 10 // All commands have at least a type. Have looked at the type, the code // typically casts the *cmd to some specific cmd type. struct cmd { int type; // ' ' (exec), | (pipe), '<' or '>' for redirection }; struct execcmd {//普通指令 int type; // ' ' char *argv[MAXARGS]; // arguments to the command to be exec-ed }; struct redircmd {//重定向 int type; // < or > struct cmd *cmd; // the command to be run (e.g., an execcmd) char *file; // the input/output file int mode; // the mode to open the file with int fd; // the file descriptor number to use for the file }; struct pipecmd {//pipe指令 int type; // | struct cmd *left; // left side of pipe struct cmd *right; // right side of pipe }; int fork1(void); // Fork but exits on failure. struct cmd *parsecmd(char*); // Execute cmd. Never returns. void runcmd(struct cmd *cmd) { int p[2], r; struct execcmd *ecmd; struct pipecmd *pcmd; struct redircmd *rcmd; if(cmd == 0) exit(0); switch(cmd->type){ default: fprintf(stderr, "unknown runcmd "); exit(-1); case ' ': ecmd = (struct execcmd*)cmd; if(ecmd->argv[0] == 0){ fprintf(stderr, "exec not implemented "); exit(0); } execvp(ecmd->argv[0], ecmd->argv); // Your code here ... break; case '>': case '<': rcmd = (struct redircmd*)cmd; // Your code here ... close(rcmd->fd); if(open(rcmd->file, rcmd->mode) < 0) { fprintf(stderr, "redir not implemented "); exit(0); } runcmd(rcmd->cmd); break; case '|': pcmd = (struct pipecmd*)cmd; //fprintf(stderr, "pipe not implemented "); // Your code here ... if(pipe(p) < 0) { fprintf(stderr, "pipe not implemented "); exit(0); } if(fork1() == 0) { close(1); dup2(p[1],1); close(p[0]); runcmd(pcmd->left); } if(fork1() == 0) { close(0); dup2(p[0],0); close(p[1]); runcmd(pcmd->right); } close(p[0]); close(p[1]); wait(); wait(); break; } exit(0); } int getcmd(char *buf, int nbuf)// 检测输入是否是标准输入流stdin,然后把用户输入存到buffer里 { if (isatty(fileno(stdin)))//fileno()函数 功能:把文件流指针转换成文件描述符; //isatty(判断文件描述词是否是为终端机)若为终端设备则返回1(真),否则返回0(假) fprintf(stdout, "$ ");//库函数 int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中。 memset(buf, 0, nbuf); fgets(buf, nbuf, stdin);//C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行, //并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。 if(buf[0] == 0) // EOF return -1; return 0; } int main(void) { static char buf[100]; int fd, r; // Read and run input commands. while(getcmd(buf, sizeof(buf)) >= 0){ if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ // Clumsy but will have to do for now. // Chdir has no effect on the parent if run in the child. buf[strlen(buf)-1] = 0; // chop if(chdir(buf+3) < 0) fprintf(stderr, "cannot cd %s ", buf+3); continue; } if(fork1() == 0) runcmd(parsecmd(buf)); wait(&r); } exit(0); } int fork1(void) { int pid; pid = fork(); if(pid == -1) perror("fork"); return pid; } struct cmd* execcmd(void)// 为一个cmd数据结构分配内存 { struct execcmd *cmd; cmd = malloc(sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd)); cmd->type = ' '; return (struct cmd*)cmd; } struct cmd* redircmd(struct cmd *subcmd, char *file, int type) { struct redircmd *cmd; cmd = malloc(sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd)); cmd->type = type; cmd->cmd = subcmd; cmd->file = file; cmd->mode = (type == '<') ? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC; cmd->fd = (type == '<') ? 0 : 1; return (struct cmd*)cmd; } struct cmd* pipecmd(struct cmd *left, struct cmd *right) { struct pipecmd *cmd; cmd = malloc(sizeof(*cmd)); memset(cmd, 0, sizeof(*cmd)); cmd->type = '|'; cmd->left = left; cmd->right = right; return (struct cmd*)cmd; } // Parsing char whitespace[] = " v"; char symbols[] = "<|>"; int gettoken(char **ps, char *es, char **q, char **eq) // 把地址ps到es的字符串中的变量找到,并存到q到eq的地址去 { char *s; int ret; s = *ps; while(s < es && strchr(whitespace, *s)) s++; if(q) *q = s; ret = *s; switch(*s){ case 0: break; case '|': case '<': s++; break; case '>': s++; break; default: ret = 'a'; while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) s++; break; } if(eq) *eq = s; while(s < es && strchr(whitespace, *s)) s++; *ps = s; return ret; } int peek(char **ps, char *es, char *toks)//判断从地址ps到es的字符串是否含有toks里面的字符 { char *s; s = *ps; while(s < es && strchr(whitespace, *s)) s++; *ps = s; return *s && strchr(toks, *s); } struct cmd *parseline(char**, char*); struct cmd *parsepipe(char**, char*); struct cmd *parseexec(char**, char*); // make a copy of the characters in the input buffer, starting from s through es. // null-terminate the copy to make it a string. char *mkcopy(char *s, char *es) // s指向需要拷贝的字符串头,es指向需要拷贝的字符串结尾. 这个函数拷贝从s到es的字符串,然后返回拷贝的地址。 { int n = es - s; char *c = malloc(n+1); assert(c); strncpy(c, s, n); c[n] = 0; return c; } struct cmd* parsecmd(char *s)// 解析命令把buffer里的命令包装成可执行的数据结构struct cmd { char *es; struct cmd *cmd; es = s + strlen(s); cmd = parseline(&s, es); peek(&s, es, ""); if(s != es){ fprintf(stderr, "leftovers: %s ", s); exit(-1); } return cmd; } struct cmd* parseline(char **ps, char *es) { struct cmd *cmd; cmd = parsepipe(ps, es); return cmd; } struct cmd* parsepipe(char **ps, char *es) { struct cmd *cmd; cmd = parseexec(ps, es); if(peek(ps, es, "|")){ gettoken(ps, es, 0, 0); cmd = pipecmd(cmd, parsepipe(ps, es)); } return cmd; } struct cmd* parseredirs(struct cmd *cmd, char **ps, char *es) { int tok; char *q, *eq; while(peek(ps, es, "<>")){ tok = gettoken(ps, es, 0, 0); if(gettoken(ps, es, &q, &eq) != 'a') { fprintf(stderr, "missing file for redirection "); exit(-1); } switch(tok){ case '<': cmd = redircmd(cmd, mkcopy(q, eq), '<'); break; case '>': cmd = redircmd(cmd, mkcopy(q, eq), '>'); break; } } return cmd; } struct cmd* parseexec(char **ps, char *es) { char *q, *eq; int tok, argc; struct execcmd *cmd; struct cmd *ret; ret = execcmd(); cmd = (struct execcmd*)ret; argc = 0; ret = parseredirs(ret, ps, es); while(!peek(ps, es, "|")){ if((tok=gettoken(ps, es, &q, &eq)) == 0) break; if(tok != 'a') { fprintf(stderr, "syntax error "); exit(-1); } cmd->argv[argc] = mkcopy(q, eq); argc++; if(argc >= MAXARGS) { fprintf(stderr, "too many args "); exit(-1); } ret = parseredirs(ret, ps, es); } cmd->argv[argc] = 0; return ret; }