基于进程控制实现简单shell命令行
实际上我们在命令行输入ls,就是-bash进程在调用fork(),而后对该子进程进行进程替换,让该子进程去执行ls的相关代码
所以我们要实现简单命令行,实际上只需下面几步
初始化命令行,打印提示信息
获取用户需求–获取用户输入的数据
解析用户需求–分析字符串
处理一些内建命令
fork子进程进行进程替换完成用户的需求
1. 初始化命令行,打印提示消息
#define NUM 64
char commond[NUM];
//将命令行初始化好
memset(commond,'\0',sizeof(commond));
//1.打印提示和清理字符串
commond[0]=0; //这个方式可以O(1)方式清理字符串
printf("[lsh@hostname mydir]# ");
fflush(stdout);
其中需要使用fflush函数,因为我们平时的命令行,打印完提示信息都是没有进行换行,而如果我们不在打印语句中+\n,我们的语句并不会马上刷新出来,而是等待进程结束后,才会刷新出来,所以我们就可以使用fllush系统调用(本质是强制刷新缓冲区–后面会讲解),将数据刷新出来
2.获取用户输入的数据
//2.获取命令 fgets
fgets(commond,sizeof(commond),stdin); //从默认入流中读取数据
//将读取到的换行换为\0 一定要取消掉 不然影响后序的输入
commond[strlen(commond)-1]='\0';
//printf("%s\n",commond); //检验输入成果
其中注意的主要是,我们结束输入时会获取回车,所以获取的子符串后面会多一个回车,需要将其去掉,会影响后面解析字符串
3.解析用户输入的字符串
//3.解析命令 strtok
char *argv[Com_NUM]={NULL};
int index=1;
char *str=" "; //分割符
argv[0]=strtok(commond,str);
//如果还有命令行参数继续获取
while(argv[index++]=strtok(NULL,str))
{}
//检验argv 检验分包的结果
// for(int i=0;argv[i];++i)
// {
// printf("argv[%d]: %s\n",i,argv[i]);
// }
// printf("\n");
4.处理一些内建命令
例如:cd …
//4.在子进程执行内置命令4. TODO,内置命令, 让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令
//内建命令本质其实就是shell中的一个函数调用
if(strcmp(argv[0],"cd")==0)
{
if(argv[1]!=NULL)
{
chdir(argv[1]);
}
continue;
}
如果用户输入的cd …,本质是需要改变当前工作目录,而如果我们进行进程替换,cd …执行的其实是在子进程执行,并不会影响当前进程,需要被shell本身执行的就叫做内建命令
chdir
int chdir(const char *path);
而实际上我们在命令行执行cd …时,也是调用了一个函数chdir,作用是将当前进程的路径变化到path
5.fork子进程进行进程替换
if(fork()==0)
{
//child
execvp(argv[0],argv);
exit(22);
}
int status=0;
waitpid(-1,&status,0);
printf("exit code:%d\n",(status>>8)&0xFF);
代码演示:
总代码:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define NUM 64
#define Com_NUM 128
int main()
{
char commond[NUM];
//将命令行初始化好
memset(commond,'\0',sizeof(commond));
while(1) //从默认入流中读取数据
{
//1.打印提示和清理字符串
commond[0]=0; //这个方式可以O(1)方式清理字符串
printf("[lsh@hostname mydir]# ");
fflush(stdout);
//2.获取命令 fgets
fgets(commond,sizeof(commond),stdin); //从默认入流中读取数据
//将读取到的换行换为\0 一定要取消掉 不然影响后序的输入
commond[strlen(commond)-1]='\0';
//printf("%s\n",commond);
//3.解析命令 strtok
char *argv[Com_NUM]={NULL};
int index=1;
char *str=" ";
argv[0]=strtok(commond,str);
//如果还有命令行参数继续获取
while(argv[index++]=strtok(NULL,str))
{}
//4.在子进程执行内置命令4. TODO,内置命令, 让父进程(shell)自己执行的命令,我们叫做内置命令,内建命令
//内建命令本质其实就是shell中的一个函数调用
if(strcmp(argv[0],"cd")==0)
{
if(argv[1]!=NULL)
{
chdir(argv[1]);
}
continue;
}
//5.在子进程中执行第三方命令
if(fork()==0)
{
//child
execvp(argv[0],argv);
exit(22);
}
int status=0;
waitpid(-1,&status,0);
printf("exit code:%d\n",(status>>8)&0xFF);
}
return 0;
}
execvp(argv[0],argv);
exit(22);
}
int status=0;
waitpid(-1,&status,0);
printf("exit code:%d\n",(status>>8)&0xFF);
}
return 0;
}