目标:
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实现仅代表个人观点。