程序描述
此程序实现了一个简单的Linux壳,支持输入各类命令参数,并为其创建进程并等待子进程结束。
程序实现思路
这个程序大致可以分为两部分 ,其一为获取用户输入的命令及参数并将其整理为数组,其二为创建子进程并调用execvp将子进程用于执行输入的命令,其中拆分得到的用户输入的字符串为难点
重难点解析
接收用户输入
接收用户的命令输入并不能简单的用scanf来实现,因为单纯的scsnf是不支持空格和tab的,而gets方法虽然能够接收空格却不被Linux系统所支持,所以我们选择了Linux所支持的gets的替代品——fgets。(更好的方法是scanf(“%[^\n]”, buffer);定义收到换行符才结束,其余的都接收。)
函数原型
char *fgets(char *buf, int bufsize, FILE *stream);
其中buf代表用于接收用户输入的字符串指针,bufsize代表接收的数据大小,stream用于指定读取流。
代码演示
fgets(cmdline,CMDLEN,stdin);
CMDLEN是我宏定义的cmdline的长度,stdin代表从键盘读取。
注意
fgets方法接收到字符串的最后带有换行符\n
字符串解析
接收到用户输入的字符串后需要根据空格将其解析,在这里我们用strtok方法来实现。
使用示例
char *args[PARAM];
for(i=0;i<PARAM;i++){
args[i]=(char*)malloc(CMDLEN);
}
char delims[]=" ";
args[0]=strtok(cmdline,delims);
//printf("%s",arg[0]);
for(i=1;i<PARAM;i++){
args[i]=strtok(NULL,delims);
if(!args[i]){
break;
}
}
其中PARAM是我宏定义的最多参数个数,首先为char*开辟空间(一定要开辟不然无法使用),然后将分隔符存入一个字符数组中,我们要用空格分割所以存的是空格。strtok第一次使用要加第一个参数为需要分割的字符串指针,以后使用则设为NULL。每次返回一段字符,最后结束返回NULL(恰好execvp最后一个参数需要为NULL,所以很方便)
戳这里了解更多strtok的知识
创建子进程
pid_t pid;
pid=fork();
if(pid==0){
if(execvp(args[0], args)<0){
printf("执行失败, errno is: %d\n", errno);
}
//errno 2 代表文件找不到,如果没有去掉fgets读回来的换行符\n就会发生这种错误;
//errno 14 代表参数最后不是空指针,注意要直接赋值为NULL而不是"NULL"或"";
exit(0);
}
else{
wait(NULL);
}
完整代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define CMDLEN 100 //命令参数最多接收100个字符
#define PARAM 10 //最多支持参数个数
/**
* [deletEnter description]
* 用于删除字符串后面的换行符
* @param str [description]
*/
void deletEnter(char *str){
int i=0;
while(1){
if(str[i]=='\n'){
str[i]='\0'; //查到有换行符就换成\0结束
break;
}else if(str[i]=='\0'){
break;
}
i++;
}
}
int main(){
char cmdline[CMDLEN];
char *args[PARAM];
while(1){
printf("[tyshell]# ");
fflush(stdout);
fgets(cmdline,CMDLEN,stdin);//不能用svanf(收不到空格)和gets(linux不支持)
/**
* 如果接收到exitq就退出,否则就解析命令创建子进程执行
*/
if(strcmp(cmdline,"exit\n")==0){
//fgets读取到的内容末尾有换行符故判断结束需要加上\n
return 0;
}else{
int i=0;
/**
* 调用方法删去字符串最后的换行符
*/
deletEnter(cmdline);
//检验接收是否有误
//printf("arglen=%d\n",arglen);
//printf("%s\n",cmdline);
//char *arg[arglen+1];
/**
* 指针初始化
*/
for(i=0;i<PARAM;i++){
args[i]=(char*)malloc(CMDLEN);
}
/**
* 使用strtok解析,拆分字符串,用法如下
*/
char delims[]={" "};
args[0]=strtok(cmdline,delims);
//printf("%s",arg[0]);
for(i=1;i<PARAM;i++){
args[i]=strtok(NULL,delims);
if(!args[i]){
break;
}
//printf("%s",arg[i]);
}
//开始多线程的执行
pid_t pid;
pid=fork();
if(pid==0){
if(execvp(args[0], args)<0){
printf("执行失败, errno is: %d\n", errno);
}
//errno 2 代表文件找不到,如果没有去掉fgets读回来的换行符\n就会发生这种错误;
//errno 14 代表参数最后不是空指针,注意要直接赋值为NULL而不是"NULL"或"";
exit(0);
}
else{
wait(NULL);
}
}
}
return 0;
}