《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的了解又深了一点。