惠州网站建设制作公司,南昌网站搭建,企业查询宝在线查询,海口网站建设小强我们制作的目标是要求自制的命令行解释器能够处理普通命令#xff0c;能够处理内建命令#xff0c;理解本地变量#xff0c;环境变量。 我们的 shell 命令是由 bash 创建子进程来执行的#xff0c;这样可以更好的保护操作系统。同理#xff0c;我们在写命令解释器时#…我们制作的目标是要求自制的命令行解释器能够处理普通命令能够处理内建命令理解本地变量环境变量。 我们的 shell 命令是由 bash 创建子进程来执行的这样可以更好的保护操作系统。同理我们在写命令解释器时用 fork 子进程的方式来完成指令父进程走主线是一个比较合理的方式。 如图这就是我们要实现的一个大体思路。 那么在 shell 命令行中应当要包括哪些内容呢
[rootlocalhost epoll]# ls
client.cpp readme.md server.cpp utility.h
[rootlocalhost epoll]# ps
PID TTY TIME CMD
3451 pts/0 00:00:00 bash
3514 pts/0 00:00:00 ps 我们需要下面这个循环过程获取命令行解析命令行建立一个子进程用execvp替换子进程父进程等待子进程退出。
下面我们就来实现一个shell了
首先我们来捋清一下思路 首先我们通过数组来存储指令信息当每次处理完一次指令之后需要对数组进行初始化初始化完数组后我们要获取当前的用户信息并将其打印下来” 用户名当前路径家目录# ” 的方式打印出来接下来我们需要获取用户输入的指令并且解析以空格为分隔符将指令存入数组当中最后是对指令进行分支处理若指令为内键命令则执行内键分支若不为内键命令就创建子进程执行命令。最后在循环返回。 一. 初始化数据
首先我们在 shell.cc 中创建全局变量 数组char *gargv和数组计数器gargc数组pwd记录输入的指令最后是退出码lastcode。
我们从系统环境变量中获取USERNAME HOSTNAME PWDHOMEPATH。
初始化时将存储指令的数组 gargv进行清0.
#include shell.h
#include iostream
#include cstdio
#include cstring
#include string.h
#include ctype.h
#include fcntl.h
#include sys/stat.h
#include stdlib.h
#include string
#include unistd.h
#include sys/types.h
#include sys/wait.h
char *gargv[ARGS] {NULL};
int gargc 0;
char pwd[1024];
int lastcode 0;static std::string GetUserName(){std::string username getenv(USER);return username.empty() ? None : username;}static std::string GetHostName(){std::string hostname getenv(HOSTNAME);return hostname.empty() ? None : hostname;}static std::string GetPwd(){char temp[1024];getcwd(temp,sizeof(temp));//将temp写入pwd并且更新内容snprintf(pwd,sizeof(pwd),PWD%s,temp);putenv(pwd);std::string pwd_lable temp;const std::string pathsep /;auto pos pwd_lable.rfind(pathsep);if(pos std::string::npos){ return None;}pwd_lable pwd_lable.substr(pospathsep.size());return pwd_lable.empty() ? / : pwd_lable;}static std::string GetHomePath(){std::string home getenv(HOME);return home.empty() ? / : home;}void InitGlobal(){gargc 0;memset(gargv,0,sizeof(gargv));}
我们通过getcwd的方式从环境变量中获得相应的值。在GetPwd处我们将temp写入pwd中重新更新环境变量的值。从temp的尾部找到第一个“/”处返回“/”后的所有字符。
二. 获取用户信息并打印
仿照我们Linux的输出方式” 用户名当前路径家目录# ”输出即可。
void PrintCommandPrompt(){std::string user GetUserName();std::string hostname GetHostName();std::string pwd GetPwd();printf([%s%s %s]# ,user.c_str(),hostname.c_str(),pwd.c_str());}三. 获取用户指令
将用户输入的指令传入数组中并把最后的回车符号去除。
bool GetCommandString(char cmd_str_buff[],int len){if(cmd_str_buff NULL || len 0)return false;char *res fgets(cmd_str_buff,len,stdin);if(res NULL)return false;cmd_str_buff[strlen(cmd_str_buff)-1]0;return strlen(cmd_str_buff)0 ? false : true;}四. 解析用户指令
以空格为分隔符用strtok将指令一一分离。
bool ParseCommandString(char cmd[]){if(cmd NULL)return false;#define SEP gargv[gargc] strtok(cmd,SEP);while((bool)(gargv[gargc]strtok(NULL,SEP)));gargc--;return true;}
五. 子进程执行命令or执行内键命令
fork创建子进程用execvp执行指令。内键命令分为两个大分支cd和echo在cd下若有两个字符当为“~”时跳转到家目录其他则更改目录到 gargv【1】处。若只有一个字符跳转到家目录。若为echo时有两个字符第一个字符为 “$”第二个字符为“”时打印最近一次退出码若第二个字符不是“”则打印环境变量。若第一个字符不为“$”打印值即可。 bool BuiltInCommandExec()
{std::string cmd gargv[0];bool ret false;if(cmd cd){if(gargc 2){std::string target gargv[1];if(target ~){ret true;chdir(GetHomePath().c_str());}else{ret true;chdir(gargv[1]);}} else if(gargc 1){ret true;chdir(GetHomePath().c_str());}else{// BUG()}}else if(cmd echo){if(gargc 2){std::string args gargv[1];if(args[0] $){if(args[1] ?){printf(lastcode:%d\n,lastcode);lastcode 0;ret true;}else{const char *name args[1];printf(%s\n,getenv(name));lastcode 0;ret true;}}else{printf(%s\n,args.c_str());ret true;}}}return ret;} 希望各位大佬多多支持