Linux应用之构建命令行解释器(bash进程)
- 人工智能
- 2025-08-26 05:18:01

目录
1.分析
2.打印输入提示符
3.读取并且处理输入字符串
4.创建子进程并切换
5.bash内部指令
6.完整代码
1.分析
当我们登录服务器的时候,命令行解释器就会自动加载出来。接下来我们就。在命令行中输入指令来达到我们想要的目的。 我们在命令行上输入的是一连串的字符串,那么bash首先要做的就是分析字符串。然后判断是否合法字符串。如果是合法字符串,那么就创建一个子进程,让子进程去切换执行命令,bash分析子进程结果。如果是非法字符串,那么就再次循环,整体嵌套在一个while循环里面。如此循环下去。
2.打印输入提示符
通过观察超级用户和普通用户可以发现提示行主要由三部分构成,用户名,主机名,地址以及特殊符号,用户名和主机名可以从环境变量中获取,主机名就设置为@区别于shell。
#include<iostream> #include<unistd.h> #include<stdio.h> #include<string> using namespace std; const int MaxSize=1024; char * UsrName; char * HostName; char CommandPwd[MaxSize]; //打印命令行提示符 void PrintCLPrompt() { printf("[%s@%s %s]@",UsrName,HostName,CommandPwd); } //准备工作 void setup() { UsrName=getenv("USER"); HostName=getenv("HOSTNAME"); //这里采用C++的string来接收是为了后序处理方便 string str=getenv("PWD"); string tmp=str.substr(str.rfind("/")+1);//加一去除/字符 snprintf(CommandPwd,MaxSize,"%s",tmp.c_str());//格式化输出到数组函数 } int main() { setup(); while(1) { PrintCLPrompt(); //让程序停在这里方便观察 scanf("%d",NULL); } return 0; }此时输出的命令行提示符就在我们的预期之中了
3.读取并且处理输入字符串读取比较简单,我们只需scanf或者cin即可。( C++兼容C所以在这里读取可以使用C的方式或者是C++的方式,同理输出也可以采用C的方式或者C++的方式哪个方便用哪个)。处理的话我们就把它处理成命令行参数列表以便后续切换进程。
前面代码相同,加上后序调试代码与读取指令即可。
//读取处理指令 char Command[MaxSize]; char * argv[MaxSize]; void ReadDealCommand() { //读取一行字符串 fgets(Command,MaxSize,stdin); int len=strlen(Command)-1;//fegts会把\n也读取到,但这个是不用的字符. Command[len]='\0'; //处理字符串 int size=0; int prev=-1,cur=0; //预处理,将空格换成\0 for(int i=0; i<len; i++) { if(Command[i]==' ') Command[i]='\0'; } while(cur<len) { if(prev == -1 && Command[cur]!='\0' || Command[prev]=='\0' && Command[cur]!='\0') { argv[size++]=Command+cur; } prev++; cur++; } //argv最后加上NULL argv[size]=NULL; } int main() { setup(); while(1) { //打印提示行 PrintCLPrompt(); //读取处理指令 ReadDealCommand(); //检测是否处理成功 for(int i=0; argv[i]; i++) { printf("argv[%d]:%s\n",i,argv[i]); } } return 0; }上述读取一行字符串采用了C语言的fgets函数,当然也可以用C++的getline。使用C++的fgets函数,要注意它会把反斜\n也读取到,在计算字符串的长度时要减一。
将原字符串空格改为\0后,就可以让数组指针直接指向原字符串即可。
其中要注意可能有结尾或者开头带空格的情况。(以下用@代替空格)
ls@-a@-l
@ls@-a@-l
ls@-a@-l@
4.创建子进程并切换//执行指令 void PreformCommand() { pid_t id=fork(); if(id ==0 ) { execvp(argv[0],argv); exit(111); } //父进程 int status=0; pid_t rid=wait(&status); if (WIFEXITED(status)) { // 子进程正常退出,提取退出状态码 int exit_status = WEXITSTATUS(status); // printf("Child process exited normally with status %d\n", exit_status); } else if (WIFSIGNALED(status)) { // 子进程因信号终止,提取信号编号 int signal_num = WTERMSIG(status); printf("Child process terminated by signal %d\n", signal_num); } } int main() { setup(); while(1) { //打印提示行 PrintCLPrompt(); //读取处理指令 ReadDealCommand(); //执行指令 PreformCommand(); } return 0; }
创建子进程,并让子进程切换到所要执行的程序,父进程接收子进程的退出码即可.由此我们便基本完成了bash程序。
5.bash内部指令有一些指令是可以直接从环境变量中得到或使用的,就不必再创建子进程了直接在bash里完成指令即可。比如cd,pwd等这些都是bash内部程序。
//执行指令 void PreformCommand() { //bash内部程序 if(strcmp("pwd",argv[0])==0) { printf("%s\n",getenv("PWD")); return ; } else if(strcmp("cd",argv[0])==0) { chdir(argv[1]); char cwd[1024]; getcwd(cwd,1024); //更新地址 string str=cwd; string tmp=str.substr(str.rfind("/")+1);//加一去除/字符 snprintf(CommandPwd,MaxSize,"%s",tmp.c_str());//格式化输出到数组函数 //修改环境变量 string s="PWD="; s+=cwd; snprintf(cwd,MaxSize,"%s",s.c_str());//格式化输出到数组函数 putenv(cwd);//putenv参数要是char*,但c_str()返回的是const char * return ; } pid_t id=fork(); if(id ==0 ) { execvp(argv[0],argv); //切换不成功返回退出码 exit(111); } //父进程 int status=0; pid_t rid=wait(&status); if (WIFEXITED(status)) { // 子进程正常退出,提取退出状态码 int exit_status = WEXITSTATUS(status); // printf("Child process exited normally with status %d\n", exit_status); } else if (WIFSIGNALED(status)) { // 子进程因信号终止,提取信号编号 int signal_num = WTERMSIG(status); printf("Child process terminated by signal %d\n", signal_num); } } int main() { setup(); while(1) { //打印提示行 PrintCLPrompt(); //读取处理指令 ReadDealCommand(); //执行指令 PreformCommand(); } return 0; }6.完整代码
最后全部的代码就在这了,上述粘贴可能有错误还望海涵
#include<iostream> #include<unistd.h> #include<stdio.h> #include<string> #include<string.h> #include<sys/types.h> #include<stdlib.h> #include<sys/wait.h> using namespace std; const int MaxSize=1024; char * UsrName; char * HostName; char CommandPwd[MaxSize]; //打印命令行提示符 void PrintCLPrompt() { printf("[%s@%s %s]@",UsrName,HostName,CommandPwd); } //准备工作 void setup() { UsrName=getenv("USER"); HostName=getenv("HOSTNAME"); string str=getenv("PWD"); string tmp=str.substr(str.rfind("/")+1);//加一去除/字符 snprintf(CommandPwd,MaxSize,"%s",tmp.c_str());//格式化输出到数组函数 } //读取处理指令 char Command[MaxSize]; char * argv[MaxSize]; void ReadDealCommand() { //读取一行字符串 fgets(Command,MaxSize,stdin); int len=strlen(Command)-1;//fegts会把\n也读取到,但这个是不用的字符 Command[len]='\0'; //处理字符串 int size=0; int prev=-1,cur=0; //预处理,将空格换成\0 for(int i=0; i<len; i++) { if(Command[i]==' ') Command[i]='\0'; } while(cur<len) { if(prev == -1 && Command[cur]!='\0' || Command[prev]=='\0' && Command[cur]!='\0') { argv[size++]=Command+cur; //.autorelabel printf("%d ",cur); } prev++; cur++; } //argv最后加上NULL argv[size]=NULL; } //执行指令 void PreformCommand() { //bash内部程序 if(strcmp("pwd",argv[0])==0) { printf("%s\n",getenv("PWD")); return ; } else if(strcmp("cd",argv[0])==0) { chdir(argv[1]); char cwd[1024]; getcwd(cwd,1024); //更新地址 string str=cwd; string tmp=str.substr(str.rfind("/")+1);//加一去除/字符 snprintf(CommandPwd,MaxSize,"%s",tmp.c_str());//格式化输出到数组函数 //修改环境变量 string s="PWD="; s+=cwd; snprintf(cwd,MaxSize,"%s",s.c_str());//格式化输出到数组函数 putenv(cwd);//putenv参数要是char*,但c_str()返回的是const char * return ; } pid_t id=fork(); if(id ==0 ) { execvp(argv[0],argv); //切换不成功返回退出码 exit(111); } //父进程 int status=0; pid_t rid=wait(&status); if (WIFEXITED(status)) { // 子进程正常退出,提取退出状态码 int exit_status = WEXITSTATUS(status); // printf("Child process exited normally with status %d\n", exit_status); } else if (WIFSIGNALED(status)) { // 子进程因信号终止,提取信号编号 int signal_num = WTERMSIG(status); printf("Child process terminated by signal %d\n", signal_num); } } int main() { setup(); while(1) { //打印提示行 PrintCLPrompt(); //读取处理指令 ReadDealCommand(); //执行指令 PreformCommand(); } return 0; }Linux应用之构建命令行解释器(bash进程)由讯客互联人工智能栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Linux应用之构建命令行解释器(bash进程)”
上一篇
Vue3中的setup