Linux编程:实现Shell环境(二)

参考书本:《Linux C 编程实战》

首先说明,这些代码,是我在学习之后共享出来的,并没有完全的自创,但是对于学习 Linux 编程非常又帮助。这篇文章的实例是为了加强对进程控制的编程强化,学过进程控制,却无法自己控制进程的,读懂这篇文章,你就会对进程控制的全部过程有所了解,但是,你必须学好进程控制的基础知识。

接上篇 Linux编程:实现Shell环境(一)

本文给出上篇程序的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#define normal		0  /* 一般的命令 */
#define out_redirect	1  /* 输出重定向 */
#define in_redirect	2  /* 输入重定向 */
#define have_pipe	3  /* 命令中有管道 */ 

void print_prompt();					/* 打印提示符 */
void get_input(char *);					/* 得到输入的命令 */
void explain_input(char *buf, int *argcount, char arglist[100][256]);		/* 对输入命令进行解析 */
void do_cmd(int argcount, char arglist[100][256]);		/* 执行命令 */
int  find_command(char *);					/* 查找命令中的可执行程序 */

int main(int argc, char **argv)
{
	int    i;
	int    argcount = 0;
	char   arglist[100][256];
	//char   **arg = NULL;
	char   *buf  = NULL;
	buf = (char *)malloc(256);
	if( buf == NULL ) {
		perror("malloc failed");
		exit(-1);
	}
	while(1) {
		/* 将buf所指向的空间清零 */
		memset(buf, 0, 256);
		print_prompt();
		get_input(buf);
		/* 若输入的命令为exit或logout则退出本程序 */
		if( strcmp(buf,"exitn") == 0 || strcmp(buf,"logoutn") == 0 )
			break;
		for (i=0; i < 100; i++)
		{
			arglist[i][0]='';

		}
		argcount = 0;
		explain_input(buf, &argcount, arglist);
		do_cmd(argcount, arglist);	

	}
	if(buf != NULL) {
		free(buf);
		buf = NULL;
	}
	exit(0);
}

void print_prompt()
{
	printf("myshell$$ ");
}

/*获取用户输入*/
void get_input(char *buf)
{
	int len = 0;
	int ch;
	ch = getchar();
	while (len < 256 && ch != 'n') {
		buf[len++] = ch;
		ch = getchar();
	}
	if(len == 256) {
		printf("command is too long n");
		exit(-1); /* 输入的命令过长则退出程序 */
	}
	buf[len] = 'n';
	len++;
	buf[len] = '';        

}

/* 解析buf中的命令,将结果存入arglist中,命令以回车符号n结束 */
/* 例如输入命令为"ls -l /tmp",则arglist[0]、arglist[1]、arglsit[2]分别为ls、-l和/tmp */
void explain_input(char *buf, int *argcount, char arglist[100][256])
{
	char	*p	= buf;
	char	*q	= buf;
	int	number	= 0;
	while (1) {
		if ( p[0] == 'n' )
			break;
		if ( p[0] == ' '  )
			p++;
		else {
			q = p;
			number = 0;
			while( (q[0]!=' ') && (q[0]!='n') ) {
				number++;
				q++;
			}
			strncpy(arglist[*argcount], p, number+1);
			arglist[*argcount][number] = '';
			*argcount = *argcount + 1;
			p = q;
		}
	}
}

void do_cmd(int argcount, char arglist[100][256])
{
	int	flag = 0;
	int	how = 0;        /* 用于指示命令中是否含有>、<、|   */
	int	background = 0; /* 标识命令中是否有后台运行标识符& */
	int	status;
	int	i;
	int	fd;
	char*	arg[argcount+1];
	char*	argnext[argcount+1];
	char*	file;
	pid_t	pid;
	/*将命令取出*/
	for (i=0; i < argcount; i++) {
		arg[i] = (char *) arglist[i];
	}
	arg[argcount] = NULL;
	/*查看命令行是否有后台运行符*/
	for (i=0; i < argcount; i++) {
		if (strncmp(arg[i], "&",1) == 0) {
			if (i == argcount-1) {
				background = 1;
				arg[argcount-1] = NULL;
				break;
			}
			else {
				printf("wrong commandn");
				return ;
			}
		}
	}
	for (i=0; arg[i]!=NULL; i++) {
		if (strcmp(arg[i], ">") == 0 ) {
			flag++;
			how = out_redirect;
			if (arg[i+1] == NULL)
				flag++;
		}
		if ( strcmp(arg[i],"<") == 0 ) {
			flag++;
			how = in_redirect;
			if(i == 0)
				flag++;
		}
		if ( strcmp(arg[i],"|")==0 ) {
			flag++;
			how = have_pipe;
			if(arg[i+1] == NULL)
				flag++;
			if(i == 0 )
				flag++;
		}
	}
	/* flag大于1,说明命令中含有多个> ,<,|符号,本程序是不支持这样的命令的
	   或者命令格式不对,如"ls -l /tmp >" */
	if (flag > 1) {
		printf("wrong commandn");
		return;
	}
	if (how == out_redirect) {  /*命令只含有一个输出重定向符号> */
		for (i=0; arg[i] != NULL; i++) {
			if (strcmp(arg[i],">")==0) {
				file   = arg[i+1];
				arg[i] = NULL;
			}
		}
	}
	if (how == in_redirect) {    /*命令只含有一个输入重定向符号< */
		for (i=0; arg[i] != NULL; i++) {
			if (strcmp (arg[i],"<") == 0) {
				file   = arg[i+1];
				arg[i] = NULL;
			}
		}
	}
	if (how == have_pipe) {  /* 命令只含有一个管道符号| */
/* 把管道符号后门的部分存入argnext中,管道后面的部分是一个可执行的shell命令 */
		for (i=0; arg[i] != NULL; i++) {
			if (strcmp(arg[i],"|")==0) {
				arg[i] = NULL;
				int j;
				for (j=i+1; arg[j] != NULL; j++) {
					argnext[j-i-1] = arg[j];
				}
				argnext[j-i-1] = arg[j];
				break;
			}
		}
	}
	if ( (pid = fork()) < 0 ) {
		printf("fork errorn");
		return;
	}
	switch(how) {
		case 0:
			/* pid为0说明是子进程,在子进程中执行输入的命令 */
			/* 输入的命令中不含>、<和| */
			if (pid == 0) {
				if ( !(find_command(arg[0])) ) {
					printf("%s : command not foundn", arg[0]);
					exit (0);
				}
				execvp(arg[0], arg);
				exit(0);
			}
			break;
		case 1:
			/* 输入的命令中含有输出重定向符> */
			if (pid == 0) {
				if ( !(find_command(arg[0])) ) {
					printf("%s : command not foundn",arg[0]);
					exit(0);
				}
				fd = open(file,O_RDWR|O_CREAT|O_TRUNC,0644);
				dup2(fd,1);
				execvp(arg[0],arg);
				exit(0);
			}
			break;
		case 2:
			/* 输入的命令中含有输入重定向符< */
			if (pid == 0) {
				if ( !(find_command (arg[0])) ) {
					printf("%s : command not foundn",arg[0]);
					exit(0);
				}
				fd = open(file,O_RDONLY);
				dup2(fd,0);
				execvp(arg[0],arg);
				exit(0);
			}
			break;
		case 3:
			/* 输入的命令中含有管道符| */
			if(pid == 0) {
				int  pid2;
				int  status2;
				int  fd2;
				if ( (pid2 = fork()) < 0 ) {
					printf("fork2 errorn");
					return;
				}
				else if (pid2==0) {
					if ( !(find_command(arg[0])) ) {
						printf("%s : command not foundn",arg[0]);
						exit(0);
					}
					fd2 = open("/tmp/youdonotknowfile",
							O_WRONLY|O_CREAT|O_TRUNC,0644);
					dup2(fd2, 1);
					execvp(arg[0], arg);
					exit(0);
				}
				if (waitpid(pid2, &status2, 0) == -1)
					printf("wait for child process errorn");
				if ( !(find_command(argnext[0])) ) {
					printf("%s : command not foundn",argnext[0]);
					exit(0);
				}
				fd2 = open("/tmp/youdonotknowfile",O_RDONLY);
				dup2(fd2,0);
				execvp (argnext[0],argnext);
				if ( remove("/tmp/youdonotknowfile") )
					printf("remove errorn");
				exit(0);
			}
			break;
		default:
			break;
	}
	/* 若命令中有&,表示后台执行,父进程直接返回不等待子进程结束 */
	if ( background == 1 ) {
		printf("[process id %d]n",pid);
		return ;
	}
	/* 父进程等待子进程结束 */
	if (waitpid (pid, &status,0) == -1)
		printf("wait for child process errorn");
}

/* 查找命令中的可执行程序 */
int find_command (char *command)
{
	DIR*             dp;
	struct dirent*   dirp;
	char *path[] = { "./", "/bin", "/usr/bin", NULL};
	/* 使当前目录下的程序可以被运行,如命令"./fork"可以被正确解释和执行 */
	if( strncmp(command,"./",2) == 0 )
		command = command + 2;
	/* 分别在当前目录、/bin和/usr/bin目录查找要可执行程序 */
	int i = 0;
	while (path[i] != NULL) {
		if ( (dp = opendir(path[i]) ) == NULL)
			printf ("can not open /bin n");
		while ( (dirp = readdir(dp)) != NULL) {
			if (strcmp(dirp->d_name,command) == 0) {
				closedir(dp);
				return 1;
			}
		}
		closedir (dp);
		i++;
	}
	return 0;
}

{ 发表评论? }

  1. 我心飞翔

    又是搞技术性的,好羡慕

  2. 疾风

    好复杂的代码 呵呵

  3. 我的美丽人生

    平平淡淡的生活里,看看别人的博客也是种消遣,还能学到东西,O(∩_∩)O

  4. 最有效的减肥产品

    额,又是怎么有技术性的东西,看不怎么懂%>_<%

  5. qvod电影

    水平不低,我是自愧不如啊,以后常来学习学习(*^__^*)

  6. BoKeam

    又一个高手

  7. 老苏

    没想到博友的水平很不错!
    我代码就不会呵呵!

  8. 某个遇到同样问题的学生

    这个管道没有实现多重啊
    ls | cat | cat
    就显示wrong command了
    还有你试试vim &
    直接terminal就失去控制了。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>