HOME | Links | About | Read

《Unix/Linux编程实践教程》笔记(8)――编写自己的shell

Table of Contents

内容

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