《Unix/Linux编程实践教程》笔记(8)――编写自己的shell
内容
这一章讲了shell的更多功能,包括变量、环境和控制逻辑,并且用代码粗略的实现了一些功能。
每个程序都从调用它的进程中继承一个字符串列表,这个列表被称为环境。/set/ 命令可以列出变量, export var 可以把变量全局化, env 可以列出所有环境变量。
习题
9.4
修改了next_cmd函数,如果输入内容中有;分隔的多条命令,那么返回第一条并且在输入内容中去掉这条,再次调用next_cmd时,返回下一条,直到处理完输入内容,再去请求新的输入。
char * next_cmd(char *prompt, FILE *fp) /* * purpose: read next command line from fp * returns: dynamically allocated string holding command line * errors: NULL at EOF (not really an error) * calls fatal from emalloc() * notes: allocates space in BUFSIZ chunks. */ { static char *buf ; /* the buffer */ static int stdinflag = 0; char *pc; char *tmppc; int bufspace = 0; /* total size */ int pos = 0; /* current position */ int c; /* input char */ if (stdinflag == 1 ){ int i = 0; while(buf[i] != '\0'){ if (buf[i] == ';') break; i++; } if (buf[i] == '\0' /*|| buf[i+1] == '\0'*/){ stdinflag = 0; return buf; } else{ pc = malloc(sizeof(char)*i); strncpy(pc, buf, i); pc[i] = '\0'; tmppc = malloc(sizeof(char)*strlen(buf)); strcpy(tmppc, buf+i+1); free(buf); buf = tmppc; return pc; } } printf("%s", prompt); /* prompt user */ while( ( c = getc(fp)) != EOF ) { /* need space? */ if( pos+1 >= bufspace ){ /* 1 for \0 */ if ( bufspace == 0 ) /* y: 1st time */ buf = emalloc(BUFSIZ); else /* or expand */ buf = erealloc(buf,bufspace+BUFSIZ); bufspace += BUFSIZ; /* update size */ } /* end of command? */ if ( c == '\n' ) break; /* no, add to buffer */ buf[pos++] = c; } buf[pos] = '\0'; if ((strchr(buf, ';')) != NULL){ stdinflag = 1; int i = 0; while(buf[i] != '\0'){ if (buf[i] == ';') break; i++; } pc = malloc(sizeof(char)*i); strncpy(pc, buf, i); tmppc = malloc(sizeof(char)*strlen(buf)); strcpy(tmppc, buf+i+1); free(buf); buf = pc[i] = '\0'; return pc; } if ( c == EOF && pos == 0 ) /* EOF and no input */ return NULL; /* say so */ return buf; }
9.5
模仿下节内容,增加一个control.c,判断是不是shell自身的命令。
int is_control_cmd(char **arglist){ printf ("arglist[0] = %s\n", arglist[0]); if ( strcmp(arglist[0], "exit") == 0) return 1; else return 0; } int do_control_cmd(char **arglist){ if ( strcmp(arglist[0], "exit") == 0){ int i = 0; int status; while (arglist[1][i] != '\0'){ if (isdigit(arglist[1][i]) != 1){ printf ("exit arg must be number\n"); return -1; } i++; } status = atoi(arglist[1]); exit(status); } }
9.6
修改controlflow.c,增加一个ELSE_BLOCK的状态,其他地方最相应修改。
#include <stdio.h> #include "smsh.h" enum states { NEUTRAL, WANT_THEN, THEN_BLOCK, ELSE_BLOCK}; enum results { SUCCESS, FAIL }; static int if_state = NEUTRAL; static int if_result = SUCCESS; static int last_stat = 0; int syn_err(char *); int ok_to_execute() /* * purpose: determine the shell should execute a command * returns: 1 for yes, 0 for no * details: if in THEN_BLOCK and if_result was SUCCESS then yes * if in THEN_BLOCK and if_result was FAIL then no * if in WANT_THEN then syntax error (sh is different) */ { int rv = 1; /* default is positive */ if ( if_state == WANT_THEN ){ syn_err("then expected"); rv = 0; } else if ( if_state == THEN_BLOCK && if_result == SUCCESS ) rv = 1; else if ( if_state == THEN_BLOCK && if_result == FAIL ) rv = 0; else if ( if_state == ELSE_BLOCK && if_result == FAIL ) rv = 1; else if ( if_state == ELSE_BLOCK && if_result == SUCCESS ) rv = 0; return rv; } int is_control_command(char *s) /* * purpose: boolean to report if the command is a shell control command * returns: 0 or 1 */ { return (strcmp(s,"if")==0 || strcmp(s,"then")==0 || strcmp(s,"fi")==0 || strcmp(s, "else")==0); } int do_control_command(char **args) /* * purpose: Process "if", "then", "fi" - change state or detect error * returns: 0 if ok, -1 for syntax error * notes: I would have put returns all over the place, Barry says "no" */ { char *cmd = args[0]; int rv = -1; if( strcmp(cmd,"if")==0 ){ if ( if_state != NEUTRAL ) rv = syn_err("if unexpected"); else { last_stat = process(args+1); if_result = (last_stat == 0 ? SUCCESS : FAIL ); if_state = WANT_THEN; rv = 0; } } else if ( strcmp(cmd,"then")==0 ){ if ( if_state != WANT_THEN ) rv = syn_err("then unexpected"); else { if_state = THEN_BLOCK; rv = 0; } } else if (strcmp(cmd, "else")==0){ if ( if_state != THEN_BLOCK) rv = syn_err("else unexpected"); else{ if_state = ELSE_BLOCK; rv = 0; } } else if ( strcmp(cmd,"fi")==0 ){ if ( if_state != THEN_BLOCK && if_state != ELSE_BLOCK) rv = syn_err("fi unexpected"); else { if_state = NEUTRAL; rv = 0; } } else fatal("internal error processing:", cmd, 2); return rv; }
9.8
我加了一个参数bg,如果末尾有&,父进程就不等待子进程返回。
9.11
这些命令(chdir,pwd)需要修改shell进程的属性,如果由子进程完成,就没法切换父进程的属性。
结束
学完这章,对shell的了解又深了一点。