8

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

 2 years ago
source link: https://houye.xyz/2018-09/uup8/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

这一章讲了shell的更多功能,包括变量、环境和控制逻辑,并且用代码粗略的实现了一些功能。

每个程序都从调用它的进程中继承一个字符串列表,这个列表被称为环境。/set/ 命令可以列出变量, export var 可以把变量全局化, env 可以列出所有环境变量。

修改了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;
}

模仿下节内容,增加一个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);
	}

}

修改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;
}

我加了一个参数bg,如果末尾有&,父进程就不等待子进程返回。

这些命令(chdir,pwd)需要修改shell进程的属性,如果由子进程完成,就没法切换父进程的属性。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK