威海外贸网站建设怎么样,wordpress没有链接地址,简述企业网站推广的一般策略,wordpress 生成文章目录 shell的基本框架PrintCommandLineGetCommandLineParseCommandLineExecuteCommandInitEnvCheckAndExecBuildCommand代码总览运行效果总结 shell的基本框架
要写一个命令行我们首先要写出基本框架。
打印命令行获取用户输入的命令分析命令执行命令
基本框架的代码
int main()
{//because shell must keep runningwhile(true){PrintCommandLine(); //1.use this func to print command line//only get command_buffer -outputGetCommandLine(); //2.get users command //is -a -b -c -d---ls -a -b -dParseCommandLine(); //3.analyze commandExecuteCommand(); //4.implement command}return 0;
}因为命令要时刻保持运行所以我们还要加上一层循环保证时时刻刻在刷新命令行。
PrintCommandLine
接下来就需要实现打印命令行的函数了首先看一下我们打印命令行时需要什么 需要用户名主机名文件路径最后还需要一个$或者#
void PrintCommandLine() //1.use this func to print command line
{//create command line //this printf have no \n,so this result is wont be display immediatelyprintf(%s,MakeCommandLine().c_str());
}打印的这个函数MakeCommandLine()负责返回一个string类型的表。 由于我们要获取主机名所以需要用到获取环境变量的函数getenv()
//get user name
string GetUserName()
{string name getenv(USER);return name.empty()?None:name;
}//get hostname
string GetHostName()
{string HostNamegetenv(HOSTNAME);return HostName.empty()?None:HostName;
}
const int basesize1024;
//overall situations The working path of the current shell
char Pwd[basesize];
//overall situations Pwd envitonment variable
char Pwdenv[basesize];
//get pwd
string GetPwd()
{//string Pwdgetenv(PWD);if(nullptr getcwd(Pwd,sizeof(Pwd))) return None;snprintf(Pwdenv,sizeof(Pwdenv),PWD%s,Pwd);putenv(Pwdenv);return Pwd;
}由于这里不能直接使用getenv(“PWD”),因为这里获取的是shell的pwdshell的pwd一直都处在当前路径下也就是我们的自己的shell运行的那个目录下所以这里这里前两个函数都很容易理解只需要解释一下最后一个我们来看看snprintf这个函数 这个函数是将后面的一个字符串以某种格式打印到前面的s当中需要写出前面s的大小。 回到获取路径这个函数当中第一个if是用来判断获取当前工作路径是否成功。如果获取成功当前工作路径将存储在Pwd当中snprintf这个函数我们将Pwd这个字符串以PWDPwd这样的格式打印这个所以这里Pwdenv已经存储了环境变量的那个格式获取了环境变量瞬时应该将环境变量表中的环境变量更新一下所以putenv最后将当前工作路径返回即可。 下面函数只需要调用上面获取好的接口即可然后按照格式化打印将对应的用户名型号名还有工作路径都存储在Command_Line这个缓冲区中然后返回到打印的函数
string MakeCommandLine()
{//[newuserhcss-ecs-e091 myshell]$char Command_Line[basesize];//outputsnprintf(Command_Line,basesize,[%s%s %s]# ,GetUserName().c_str(),GetHostName().c_str(),GetPwd().c_str());return Command_Line;
}PrintCommandLine 这里打印的时候需要刷新的一下屏幕保证一直都是在一行
void PrintCommandLine() //1.use this func to print command line
{//create command line //this printf have no \n,so this result is wont be display immediatelyprintf(%s,MakeCommandLine().c_str());//refresh screenfflush(stdout);
}GetCommandLine
获取命令行首先需要一个数组将输入的命令先存放在这个函数中然后由下一步来执行将字符串拆分为单个命令和选项即可。
//after only call this commandline,put string to this buffer
bool GetCommandLine(char Command_Buffer[],int size) //2.get users command
{//cannot be use scanf and cin //we think:We need to treat the command line entered by the user as a complete string //ls -a -l -n this is a complete string // array size stadard inputchar *result fgets(Command_Buffer,size,stdin);if(!result) return false;//we should delete last str,because it is enter keyCommand_Buffer[strlen(Command_Buffer)-1] 0;//currently, it is OKif(strlen(Command_Buffer) 0) return false;return true;
}这里不能用scanf因为scanf和cin不能使用空格所以我们选择用fgets用fgets获取字符串然后将这个字符串存在Command_Buffer中获取完之后判断一下是否获取成功就是检查一下获取之后的变量是否是nullptr。
注意最后一个位置的字符需要改为0因为我们输入的时候会回车也是一个字符所以应该将这个回车给去掉 当获取的命令中只有回车的时候将回车去掉strlen就变为0了所以只有回车时不需要解析命令所以直接返回false。
ParseCommandLine
获取成功后我们就需要将整个字符串以空格为分隔符将其分为各个字符串来进行解析了。 这里转化为的表在shell中就是传给main参数的argc和argv所以这里我们也需要一个变量argc和一个argv一个来计数一个来存储解析出来的表。
const int argvnum 64;
//command line parameter list
char *gargv[argvnum];
//for counting
int gargc;由于我们使用的是全局变量所以每次进入这个函数的时候都需要重置这两个全局变量
void ParseCommandLine(char Command_Buffer[],int len) //3.analyze command
{(void)len;memset(gargv,0,sizeof(gargv));gargc0;//ls -a -l -n----ls//finally cut to Cogargv const char *sep ;//postgargv[gargc] strtok(Command_Buffer,sep);//Form a table and stop looping when the return value is nullptr while((bool)(gargv[gargc] strtok(nullptr,sep)));gargc--;
}这个函数是将str以delimiters为分隔符来分割字符串分出来的字符串会返回首地址第一个参数只有第一次才传入对应的字符串的首地址往后调用这个函数对同一个字符串做分割只需要传入nullptr所以第一个较为特殊我们只需要对第一个做特殊处理将其第一个分割然后存储在gargv中然后对应的计数往后都是nullptr作为第一个参数往后分割之后返回的是nullptr就证明分割完了所以这里我们要使用一个循环但是由于nullptr那次也进行了所以实际上计数多记了一次下面要进行–。
ExecuteCommand
将对应的字符串根据空格分隔符翻译为表之后接下来就需要执行命令了为了确保shell的稳定所以我们用子进程来执行命令这里创建子进程然后判断子进程是否创建成功创建成功后子进程执行任务这里最开始其实可以用execvp来进行进程替换gargv是指令gargv是整个选项。 由于进行进程替换之后就不可能执行exit了所以exit是用来判断替换失败还是替换成功的如果替换成功就不会exit了如果失败就会退出并且退出码是1父进程等待子进程结束回收子进程即可如果rid为正说明回收成功如果rid小于零等待失败直接返回false
bool ExecuteCommand() //4.implement command
{//implement command //let the child process execute //because parent process execute the process ,if this process is failed ,myshell is hangspid_t id fork();//create childif(id 0) return false;if(id 0){//child process//implement command execvpe(gargv[0],gargv,genv);//Exitexit(1);//fail return 1}int status 0;pid_t rid waitpid(id,status,0);//blocking wait if(rid 0){//wait successreturn true;}else return false;
}这里其实已经差不多了但是我们还需要初始化我们的环境变量表。
InitEnv
需要顶一个全局的环境变量表
//my env array
const int envnum 64;
char *genv[envnum];拥有我们自己的环境变量只需要将父进程的环境变量拷贝下来即可进行深拷贝。
//as a shell,to get a evironment variable should from system to get
//today, we direct get environment variable from parent process
void InitEnv()
{//get environment variables from parent process extern char **environ;int index 0;while(environ[index] ! nullptr){//open up the same space as environment variable genv[index] (char*)malloc(strlen(environ[index])1);//copy element in genv to environ strncpy(genv[index],environ[index],strlen(environ[index]1));index;}genv[index]nullptr;
}我们还需要对一些命令进行特殊处理比如一些内建命令。
CheckAndExecBuildCommand
因为我们是用子进程执行的命令所以如果我们cd的话是子进程cd影响不了父进程子进程执行完cd直接退出了所以我们需要一个函数来判断这个命令是否是内建命令 内建命令是指直接在 shell 内部实现的命令而不是外部可执行文件。内建命令是由 shell 本身提供和执行的因此它们不需要创建新的进程来执行。相比于外部命令内建命令的执行速度更快因为它们不需要通过系统调用加载可执行文件。内建命令通常用于控制 shell 的行为或执行与 shell 相关的任务。
//shell execute command by itself,the essence is shell call itselfs func
bool CheckAndExecBuildCommand()//check build-in command and execute command
{if(strcmp(gargv[0],cd) 0){//build-in commandif(gargc 2){//change path chdir(gargv[1]);}return true;}//export is also a build-in command else if(strcmp(gargv[0],export) 0){if(gargc 2){AddEnv(gargv[1]);}return true;}//env is also a build-in command else if(strcmp(gargv[0],env) 0){for(int i 0;genv[i];i){printf(%s\n,genv[i]);}return true;}return false;
}代码总览
#includeiostream
#includecstdio
#includecstdlib
#includecstring
#includestring
#includeunistd.h
#includesys/types.h
#includesys/wait.h
#includecstdlib
using namespace std;const int basesize1024;
const int argvnum 64;
//command line parameter list
char *gargv[argvnum];//for counting
int gargc;//overall situations The working path of the current shell
char Pwd[basesize];
//overall situations Pwd envitonment variable
char Pwdenv[basesize];//my env array
const int envnum 64;
char *genv[envnum];//get user name
string GetUserName()
{string name getenv(USER);return name.empty()?None:name;
}//get hostname
string GetHostName()
{string HostNamegetenv(HOSTNAME);return HostName.empty()?None:HostName;
}//get pwd
string GetPwd()
{//string Pwdgetenv(PWD);if(nullptr getcwd(Pwd,sizeof(Pwd))) return None;snprintf(Pwdenv,sizeof(Pwdenv),PWD%s,Pwd);putenv(Pwdenv);return Pwd;
}//Create output format
string MakeCommandLine()
{//[newuserhcss-ecs-e091 myshell]$char Command_Line[basesize];//outputsnprintf(Command_Line,basesize,[%s%s %s]# ,GetUserName().c_str(),GetHostName().c_str(),GetPwd().c_str());return Command_Line;
}void PrintCommandLine() //1.use this func to print command line
{//create command line //this printf have no \n,so this result is wont be display immediatelyprintf(%s,MakeCommandLine().c_str());//refresh screenfflush(stdout);
}//after only call this commandline,put string to this buffer
bool GetCommandLine(char Command_Buffer[],int size) //2.get users command
{//cannot be use scanf and cin //we think:We need to treat the command line entered by the user as a complete string //ls -a -l -n this is a complete string // array size stadard inputchar *result fgets(Command_Buffer,size,stdin);if(!result) return false;//we should delete last str,because it is enter keyCommand_Buffer[strlen(Command_Buffer)-1] 0;//currently, it is OKif(strlen(Command_Buffer) 0) return false;return true;
}void ParseCommandLine(char Command_Buffer[],int len) //3.analyze command
{(void)len;memset(gargv,0,sizeof(gargv));gargc0;//ls -a -l -n----ls//finally cut to Cogargv const char *sep ;//postgargv[gargc] strtok(Command_Buffer,sep);//Form a table and stop looping when the return value is nullptr while((bool)(gargv[gargc] strtok(nullptr,sep)));gargc--;
}//in my command line
//have some command must be child process to implement
//but have some command not be child process to implement----built-in command
bool ExecuteCommand() //4.implement command
{//implement command //let the child process execute //because parent process execute the process ,if this process is failed ,myshell is hangspid_t id fork();//create childif(id 0) return false;if(id 0){//child process//implement command execvpe(gargv[0],gargv,genv);//Exitexit(1);//fail return 1}int status 0;pid_t rid waitpid(id,status,0);//blocking wait if(rid 0){//wait successreturn true;}else return false;
}//add a environment variable
void AddEnv(const char *item)
{int index 0;while(genv[index]) index;//find last location genv[index] (char*)malloc(strlen(item)1);strncpy(genv[index],item,strlen(item)1);genv[index] nullptr;}//shell execute command by itself,the essence is shell call itselfs func
bool CheckAndExecBuildCommand()//check build-in command and execute command
{if(strcmp(gargv[0],cd) 0){//build-in commandif(gargc 2){//change path chdir(gargv[1]);}return true;}//export is also a build-in command else if(strcmp(gargv[0],export) 0){if(gargc 2){AddEnv(gargv[1]);}return true;}//env is also a build-in command else if(strcmp(gargv[0],env) 0){for(int i 0;genv[i];i){printf(%s\n,genv[i]);}return true;}return false;
}//as a shell,to get a evironment variable should from system to get
//today, we direct get environment variable from parent process
void InitEnv()
{//get environment variables from parent process extern char **environ;int index 0;while(environ[index] ! nullptr){//open up the same space as environment variable genv[index] (char*)malloc(strlen(environ[index])1);//copy element in genv to environ strncpy(genv[index],environ[index],strlen(environ[index]1));index;}genv[index]nullptr;
}int main()
{//my shells environment variable InitEnv();//new buffer char Command_Buffer[basesize];//because shell must keep runningwhile(true){PrintCommandLine(); //1.use this func to print command line//only get command_buffer -outputif(!GetCommandLine(Command_Buffer,basesize)) //2.get users command {//get fail continue; }//is -a -b -c -d---ls -a -b -dParseCommandLine(Command_Buffer,strlen(Command_Buffer)); //3.analyze commandif(CheckAndExecBuildCommand()){continue;}ExecuteCommand(); //4.implement command}return 0;
}运行效果 总结
通过编写一个简易版的Linux命令行shell我们掌握了在命令行环境中解析并运行指令的基础知识。这一项目帮助我们理解了如何通过系统调用执行外部程序、处理输入和输出以及如何让shell与用户交互。尽管功能较为基础但它包含了命令读取、解析和执行等关键流程为后续学习更复杂的shell实现和系统编程提供了扎实的基础。如果有兴趣进一步扩展可以尝试加入更多特性如命令历史记录、自动补全、管道和重定向支持等使这个shell更加功能丰富。