当前位置: 首页 > 工具软件 > shell-history > 使用案例 >

<操作系统>C语言简单实现UNIX Shell and History Feature

商冠玉
2023-12-01

目标:
1.实现简单shell命令,并识别是否带有&(后台执行);
2.实现历史记录功能,并能使用!+num执行第num条命令,!!执行上一条命令。

实现思路:
1.接收命令(字符串),并分割为命令+参数形式:
可以使用库函数strtok()分割,strtok()详情

使用字符指针数组args存储,注意最后置为NULL

p = strtok(str," ");
	while(p){
		args[i] = p;
		p = strtok(NULL," ");
		i++;
	}
	args[i] = NULL;

这样命令与参数被存入args数组中,其中args[0]为命令,后面为参数。

2.创建while永真循环,通过fork()系统调用创建子进程,fork()使用:fork()简述
如果pid == 0 ,则当前为子进程,使用函数execvp()执行命令,execvp()使用:execvp()简述

pid_t pid = fork();
	if(pid == 0){		//in chlid
		execvp(args[0],args);
		exit(1);
	}else{			//in parent
		if(!flag){	//flag作为是否有&标志
		wait(pid);
		}
	}

3.判断命令是否带有&,命令是已经被分割在args里,通过一个for循环遍历索引即可,这里不多赘述。

至此一个简单的shell就完成了,下面介绍history历史记录功能的简单实现

1.history存储命令的存储结构非常重要,需要显示最近的10条记录,所以不能单单使用数组存储。最好的,应该让数组下标mod10循环。然而不能使用字符指针数组,原因参考上一篇博客:C/C++ 字符指针数组循环赋值问题

那么我们使用最原始的二维字符数组存储,简单操作下标实现循环队列,这里进行了初始化,方便后续遍历查找。

char history[10][40] = {'\0'};

定义一个全局变量t作为history数组下标,记录当前命令位置;检索关键字‘history’打印历史记录。

int t=0;
char* preExeHis(char com[],char his[][40]){
	int i=t,j=0;
	if(!strcmp(com,"history")){
		i<10?i=0:t;
		while(strcmp(his[i],"\0") && j<10){
			printf("%s\n",his[i]);
			i = (i+1)%10;
			j++;
		}
		return NULL;	
	}
}

当然每次获取到命令时记得存储,这里通过对下标进行取模操作,简单实现队列。

	strcpy(history[t],p);
	preExeHis(str,history);
	t = (t+1) % 10;

这时历史记录history功能就基本实现了(当然有些小bug就自己优化了)。

2.接下来检索!!实现执行上一条命令功能,这里就检索字符串是否为"!!",然后将history中上一条记录copy给str再进行字符串分割等操作就行了。

3.最后实现!+num进行执行第数字条的命令,还是通过strtok()函数分割出!后面的数字,再返回对应history里的命令就行了。

char* preExePoint(char com[],char his[][40]){
	char *pnum;
	int num;
	pnum = strtok(com,"!");
	if(!pnum){
		printf("No such command in history!\n");
		exit(1);
	}
	num = *pnum -'0';
	if(num < 10 && num >= 0){
		return his[num-1];
	}else{
		return com;
	}
}

4.错误检索,如果用户没有输入命令,跳过本次循环;

if(str[0] == '\0'){//no command
		continue;
	}

如果仅输入一个!,通过判断数字指针为空,打印错误信息。C语言空指针判断

if(!pnum){
		printf("No such command in history!\n");
		exit(1);
	}

源代码:请待更新…

注:至此功能基本实现,当然仍有不少bug,源代码会在后续添加上来,本shell实现仅代表个人观点。

 类似资料: