Linux实现简易版Shell的代码详解

Written by

in

文章目录
  • 我们日常使用Bash时,通过输入命令执行相应的操作,比如: 那么,Bash是如何进行工作的呢?观察一下,就会发现,首先Bash会打印命令行提示符,包括当前用户、主机名以及路径。之后会等待我们输入相关命令,然后根据命令执行相应程序。程序执行结束后,就会再次打印命令行提示符,等待我们再次输入指令…很明显是一个死循环。 总结一下,Bash的大体工作流程是: 1.打印命令行提示符(包括当前用户名、主机名、路径)2.获取用户输入的命令行3.解析命令行4.执行命令5.继续打印命令行提示符… 注意:当Bash执行非内建命令时,会创建一个子进程,由子进程完成相应的工作,Bash自己等待子进程工作结束。而对于内建命令(如cd,echo),需要Bash自己执行任务。
  • 接下来,我们开始按照上述工作流程,一步步实现我们的简易Shell。
  • 接下来,我们输入一些指令进行测试,看看我们实现的shell能否达到预期效果: perfect!
  • 自实现shell的全部代码如下: #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> //全局变量存储命令行参数及其个数 int g_argc = 0; char* g_argv[128]; //全局的当前工作路径 char cwd[256]; //全局变量记录上一个程序的退出码 int exit_code; //获取当前用户名 const char* GetUserName() { const char* name = getenv(“USER”); return name == nullptr ? “none” : name; } //获取当前主机名 const char* GetHostName() { const char* name = getenv(“HOSTNAME”); return name == nullptr ? “none” : name; } //获取当前工作路径 const char* GetPwd() { const char* pwd = getenv(“PWD”); return pwd == nullptr ? “none” : pwd; } //获取用户的家目录 const char* GetHome() { const char* home = getenv(“HOME”); return home == nullptr ? “” : home; } //创建并打印命令行提示符 void PrintCommandPrompt() { char CommandPrompt[1024]; //处理当前工作路径 std::string pwd = GetPwd(); if(pwd != “/”) // 如果是根目录,则直接输出 pwd = pwd.substr(pwd.rfind(‘/’) + 1); // 查找最后一个”/”,并从下一个位置开始分割 //这里为了区分Bash,用大括号 snprintf(CommandPrompt, sizeof CommandPrompt,”{%s@%s %s}@ ” , GetUserName(), GetHostName(), pwd.c_str()); std::cout << CommandPrompt << std::flush; // 打印并刷新缓冲区 } //获取用户输入的命令行 bool GetCommandLine(char* command, int size) { //从键盘读取命令 if(fgets(command, size, stdin) == nullptr) { return false; } //注意清理末尾的’n’ if(strlen(command) == 0) return false; command[strlen(command) – 1] = ”; return true; } //命令行解析 bool CommandParse(char* command) { g_argc = 0; for(char* p = strtok(command, ” “); p != nullptr; p = strtok(nullptr, ” “)) { g_argv[g_argc++] = p; } return g_argc == 0 ? false : true; } void Cd() { std::string dst; if(g_argc == 1 || g_argv[1] == std::string(“~”)) // 处理进入家目录的情况 { dst = GetHome(); if(dst == “”) return; } else { dst = g_argv[1]; } chdir(dst.c_str()); //同时修改环境变量中的当前工作路径 char tmp[128]; if(getcwd(tmp, sizeof tmp)) { snprintf(cwd, sizeof cwd, “PWD=%s”, tmp); putenv(cwd); } } void Echo() { std::string op = g_argv[1]; if(op == “$?”) // 打印上一个程序的退出码 { std::cout << exit_code << std::endl; exit_code = 0; } else if(op[0] == ‘$’) // 打印环境变量的值 { //从”$”后的第一个字符开始,获取环境变量名 std::string name = op.substr(1); //用getenv获取环境变量值 const char* value = getenv(name.c_str()); if(value) std::cout << value << std::endl; } else // 打印字符串 { std::cout << op << std::endl; } } //内建命令处理 bool CheckAndExecBuiltin() { //取出命令行参数表的首元素,判断是否为内建命令,如果是,则直接执行 std::string str = g_argv[0]; if(str == “cd”) { Cd(); return true; } else if(str == “echo”) { Echo(); return true; } //else if(…) return false; // 不是内建命令,直接返回 } //执行非内建命令 void Execute() { //创建子进程 pid_t id = fork(); if(id < 0) { //创建子进程失败,直接退出 exit(1); } else if(id == 0) { //子进程 — 通过程序替换执行指令 execvp(g_argv[0], g_argv); } else { //父进程 — 等待子进程,并获取子进程的退出信息 int status = 0; pid_t rid = waitpid(id, &status, 0); if(rid > 0) // 等待成功 { //将退出信息存入全局exit_code exit_code = WEXITSTATUS(status); } } } int main() { while(true) { //打印命令行提示符 PrintCommandPrompt(); //读入命令 char command[1024]; if(!GetCommandLine(command, sizeof command)) // 若读取错误,就continue重新读取 { std::cout << “输入错误” << std::endl; continue; } // //打印输入的命令行 // std::cout << command << std::endl; //命令行解析 if(!CommandParse(command)) { std::cout << “命令行解析失败” << std::endl; continue; } // //打印解析结果 // for(int i = 0; i < g_argc; i++) // { // std::cout << g_argv[i] << std::endl; // } //内建命令的处理 if(CheckAndExecBuiltin()) { continue; // 是内建命令,执行完毕就回去重新打印提示符 } //不是内建命令,由子进程处理 Execute(); } return 0; }
  • 本篇文章,我们基于之前学习的Linux进程相关概念以及进程控制接口,实现了一个简易版的shell。本次实现让我们对Shell的运行原理有了更深刻的理解。 以上就是Linux实现简易版Shell的代码详解的详细内容,更多关于Linux简易版Shell的资料请关注风君子博客其它相关文章! 您可能感兴趣的文章: 分享个简易版Linux服务器初始化Shell脚本 Linux实现自主Shell命令行解释器 Linux下Shell编程之sed命令的用法详解 Linux上读取或设置环境和shell变量的教程
  • 目录
    • 一、程序流程分析
    • 二、代码实现
      • 1. 打印命令行提示符
      • 2. 获取用户输入的命令行
      • 3. 命令行解析
      • 4. 执行命令
        • 内建命令的处理
        • cd的简易实现
    • 三、测试
      • 四、程序全部代码
        • 总结

          我们日常使用Bash时,通过输入命令执行相应的操作,比如:

          在这里插入图片描述

          那么,Bash是如何进行工作的呢?观察一下,就会发现,首先Bash会打印命令行提示符,包括当前用户、主机名以及路径。之后会等待我们输入相关命令,然后根据命令执行相应程序。程序执行结束后,就会再次打印命令行提示符,等待我们再次输入指令…很明显是一个死循环。

          总结一下,Bash的大体工作流程是:

          1.打印命令行提示符(包括当前用户名、主机名、路径)
          2.获取用户输入的命令行
          3.解析命令行
          4.执行命令
          5.继续打印命令行提示符…

          注意:当Bash执行非内建命令时,会创建一个子进程,由子进程完成相应的工作,Bash自己等待子进程工作结束。而对于内建命令(如cd,echo),需要Bash自己执行任务。

          ![[Pasted image 20250518164821.png]]

          接下来,我们开始按照上述工作流程,一步步实现我们的简易Shell。

          CentOS的命令行提示符主要包含三个内容:当前用户名、主机名和当前所在路径。 之前学习Linux环境变量时,我们了解到环境变量(USER、HOSTNAME、PWD)中存储着这些内容。所以我们使用getenv函数获取环境变量相应的值。

          代码实现:

          #include <iostream>
          #include <cstdio>
          #include <cstring>
          #include <cstdlib>
          #include <unistd.h>
          #include <sys/types.h>
          #include <sys/wait.h>
          
          //获取当前用户名
          const char* GetUserName()
          {
              const char* name = getenv("USER");
              return name == nullptr ? "none" : name;
          }
          
          //获取当前主机名
          const char* GetHostName()
          {
              const char* name = getenv("HOSTNAME");
              return name == nullptr ? "none" : name;
          }
          
          //获取当前工作路径
          const char* GetPwd()
          {
              const char* pwd = getenv("PWD");
              return pwd == nullptr ? "none" : pwd;
          }
          

          接下来,调用这些函数,形成一串命令行提示符并打印:

          //创建并打印命令行提示符
          void PrintCommandPrompt()
          {
              char CommandPrompt[1024];
              //这里为了区分Bash,用大括号
              snprintf(CommandPrompt, sizeof CommandPrompt,"{%s@%s %s}@ " , GetUserName(), GetHostName(), GetPwd());
              std::cout << CommandPrompt << std::flush; // 打印并刷新缓冲区
          }
          

          注意这里打印结束后,由于没有换行,所以缓冲区可能不会刷新,导致命令行提示符没有出现在屏幕上。因此这里我们需要主动刷新缓冲区。

          进行测试:

          int main()
          {
              PrintCommandPrompt();
              return 0;
          }
          

          运行结果:

          obsidian://open?vault=%E5%8D%9A%E5%AE%A2&file=Pasted%20image%2020250518155420.png

          可以看到,用户名和主机名都正常打印,但在我们写的shell中,当前所在路径是绝对路径,太过冗长,所以可以对生成的路径进行一些处理:

          //创建并打印命令行提示符
          void PrintCommandPrompt()
          {
              char CommandPrompt[1024];
              
              //处理当前工作路径
              std::string pwd = GetPwd();
              if(pwd != "/") // 如果是根目录,则直接输出
                  pwd = pwd.substr(pwd.rfind('/') + 1); // 查找最后一个"/",并从下一个位置开始分割
          
              //这里为了区分Bash,用大括号
              snprintf(CommandPrompt, sizeof CommandPrompt,"{%s@%s %s}@ " , GetUserName(), GetHostName(), pwd.c_str());
              std::cout << CommandPrompt << std::flush; // 打印并刷新缓冲区
          }
          

          运行结果:

          ![[Pasted image 20250518161526.png]]

          在Bash输入命令时,往往会带上一些选项,并用空格隔开。因此,使用scanf或cin读入时会以空格作为分隔符,达不到想要的效果。这里我们选择用fgets进行读取。

          另外,主函数当中,命令的全部处理应该放在一个死循环当中,这样才能完成用户多次派发的任务。

          //获取用户输入的命令行
          bool GetCommandLine(char* command, int size)
          {
              //从键盘读取命令
              if(fgets(command, size, stdin) == nullptr)
              {
                  return false;
              }
          
              //注意清理末尾的'n'
              if(strlen(command) == 0) return false;
              command[strlen(command) - 1] = '';
              return true;
          }
          
          int main()
          {
              while(true)
              {
                  //打印命令行提示符
                  PrintCommandPrompt();
          
                  //读入命令
                  char command[1024];
                  if(!GetCommandLine(command, sizeof command)) // 若读取错误,就continue重新读取
                  {
                      std::cout << "读取错误" << std::endl;
                      continue;
                  }
          
                  //打印输入的命令行
                  std::cout << command << std::endl;
              }
              return 0;
          }
          
          

          注意:使用fgets从键盘读取字符串时会附带末尾’n’,需要进行处理。

          运行测试:

          ![[Pasted image 20250518165317.png]]

          可以看到,程序成功读入了我们的命令(包括空格),并且将命令回显出来。

          命令行解析的过程中,需要将用户读入的命令行进行分割,提取出要执行的程序名以及选项。这里我们创建两个全局变量g_argc和g_argv,分别存储解析到的命令行参数以及参数个数,方便后续指令的执行。

          注:这里的命令行分割操作由strtok函数完成。

          代码实现:

          //全局变量存储命令行参数及其个数
          int g_argc = 0;
          char* g_argv[128];
          
          //命令行解析
          bool CommandParse(char* command)
          {
              g_argc = 0;
              for(char* p = strtok(command, " "); p != nullptr; p = strtok(nullptr, " "))
              {
                  g_argv[g_argc++] = p;
              }
              return g_argc == 0 ? false : true;
          }
          
          int main()
          {
              while(true)
              {
                  //打印命令行提示符
                  PrintCommandPrompt();
          
                  //读入命令
                  char command[1024];
                  if(!GetCommandLine(command, sizeof command)) // 若读取错误,就continue重新读取
                  {
                      std::cout << "输入错误" << std::endl;
                      continue;
                  }
                  // //打印输入的命令行
                  // std::cout << command << std::endl;
          
                  //命令行解析
                  if(!CommandParse(command))
                  {
                      std::cout << "命令行解析失败" << std::endl;
                      continue;
                  }
                  //打印解析结果
                  for(int i = 0; i < g_argc; i++)
                  {
                      std::cout << g_argv[i] << std::endl;
                  }
              }
              return 0;
          }
          

          测试结果:

          ![[Pasted image 20250518173801.png]]

          程序成功地按照空格将我们输入的命令行参数提取了出来。接下来,根据提取到的参数,就可以执行相关指令了。

          对于非内建命令,Bash会创建子进程,并让子进程执行;而对于内建命令,则是由Bash自己执行。因此,执行命令之前,需要先判断该命令是否是内建命令,然后进行相应的操作。

          为什么会有内建命令?

          • 效率: 执行内建命令通常比执行外部命令更快,因为避免了创建新进程的开销。
          • Shell 功能: 许多内建命令直接操作 Shell 的内部状态,例如改变当前工作目录 (cd)、设置环境变量 (export)、控制 Shell 行为等,这些功能如果作为外部命令实现会更加复杂或不可能。
          • 基本操作: 一些非常基础和常用的操作需要作为内建命令提供,以确保 Shell 的基本功能可用

          在Bash当中,可以使用type命令判断一个命令是否是内建命令,例如:

          ![[Pasted image 20250518174929.png]]

          当然,除了cd和echo命令,还有printf、help等内建命令。本次实现中,为了能够让大家深刻理解shell运行原理,同时降低实现难度,博主就只针对cd和echo这两个内建命令进行简易实现。

          内建命令的检查和处理:

          //内建命令处理
          bool CheckAndExecBuiltin()
          {
              //取出命令行参数表的首元素,判断是否为内建命令,如果是,则直接执行
              std::string str = g_argv[0];
              if(str == "cd")
              {
                  Cd();
                  return true;
              }
              else if(str == "echo")
              {
                  Echo();
                  return true;
              }
              //else if(...)
              return false; // 不是内建命令,直接返回
          }
          
          int main()
          {
              while(true)
              {
                  //打印命令行提示符
                  PrintCommandPrompt();
          
                  //读入命令
                  char command[1024];
                  if(!GetCommandLine(command, sizeof command)) // 若读取错误,就continue重新读取
                  {
                      std::cout << "输入错误" << std::endl;
                      continue;
                  }
                  // //打印输入的命令行
                  // std::cout << command << std::endl;
          
                  //命令行解析
                  if(!CommandParse(command))
                  {
                      std::cout << "命令行解析失败" << std::endl;
                      continue;
                  }
                  // //打印解析结果
                  // for(int i = 0; i < g_argc; i++)
                  // {
                  //     std::cout << g_argv[i] << std::endl;
                  // }
          
                  //内建命令的处理
                  if(CheckAndExecBuiltin())
                  {
                      continue; // 是内建命令,执行完毕就回去重新打印提示符
                  }
          
                  //不是内建命令,由子进程处理
              }
              return 0;
          }
          

          cd的功能是改变当前工作路径。如果创建子进程,其只能修改它自己的工作路径,而无法修改Bash的工作路径。因此cd操作需要Bash亲自完成。 我们获取到命令行参数后,可以通过调用chdir函数实现:

          void Cd()
          {
              std::string dst;
              if(g_argc == 1 || g_argv[1] == std::string("~")) // 处理进入家目录的情况
              {
                  dst = GetHome();
                  if(dst == "") return;
              }
              else
              {
                  dst = g_argv[1];
              }
              chdir(dst.c_str());
          }
          

          测试结果:

          ![[Pasted image 20250518190207.png]]

          可以看到,使用cd后,当前路径貌似并没有发生改变。为什么呢?实际上chdir确实起到了效果,但是我们的命令行提示符中,当前工作路径是从环境变量中获取的,环境变量中的PWD并没有发生改变。 因此,修改当前工作路径之后,要顺带着修改环境变量PWD的值。其次,当前工作路径改变后,修改环境变量之前,要获取到当前工作路径,就需要使用getcwd函数。进行键值处理后,使用putenv修改环境变量。

          另外要注意:许多putenv函数的实现(特别是 POSIX 标准的实现)不会复制我们传递给它的字符串,而会直接使用传入的指针来表示新的环境变量,因此要创建一个全局变量存储PWD键值对,确保其不会被销毁。

          ![[Pasted image 20250518193953.png]]

          代码实现:

          //全局的当前工作路径
          char cwd[256];
          
          void Cd()
          {
              std::string dst;
              if(g_argc == 1 || g_argv[1] == "~") // 处理进入家目录的情况
              {
                  dst = GetHome();
                  if(dst == "") return;
              }
              else
              {
                  dst = g_argv[1];
              }
              chdir(dst.c_str());
          
              //同时修改环境变量中的当前工作路径
              char tmp[128];
              if(getcwd(tmp, sizeof tmp))
              {
                  snprintf(cwd, sizeof cwd, "PWD=%s", tmp); // 键值处理
                  putenv(cwd);
              }
          }
          

          测试结果:

          ![[Pasted image 20250518200749.png]]

          echo的简易实现

          echo不仅可以向屏幕打印字符串,还可以打印上一个执行的程序的退出码、以及环境变量等信息。为了确保效率以及执行的可靠性,该命令也是一个内建命令,由Bash亲自执行。

          //全局变量记录上一个程序的退出码
          int exit_code;
          
          void Echo()
          {
              std::string op = g_argv[1];
              if(op == "$?") // 打印上一个程序的退出码
              {
                  std::cout << exit_code << std::endl;
                  exit_code = 0;
              }
              else if(op[0] == '$') // 打印环境变量的值
              {
                  //从"$"后的第一个字符开始,获取环境变量名
                  std::string name = op.substr(1);
          
                  //用getenv获取环境变量值
                  const char* value = getenv(name.c_str());
                  if(value)
                      std::cout << value << std::endl;
              }
              else // 打印字符串
              {
                  std::cout << op << std::endl;
              }
          }
          

          测试结果:

          ![[Pasted image 20250518200451.png]]

          非内建命令的处理

          针对非内建命令,需要创建子进程,然后由子进程找到系统的对应指令程序的位置,直接进行进程程序替换,完成相关任务。这样我们就无需一个个实现相关指令操作了。

          为了让子进程自动查找对应程序的位置,减少我们的工作量,且使程序替换的参数与我们创建的全局命令行参数表一一对应,这里我们直接选用execvp进行程序替换。

          #include <unistd.h>
            
          int execvp(const char *file, char *const argv[])
          

          代码实现:

          //执行非内建命令
          void Execute()
          {
              //创建子进程
              pid_t id = fork();
              if(id < 0)
              {
                  //创建子进程失败,直接退出
                  exit(1);
              }
              else if(id == 0)
              {
                  //子进程 -- 通过程序替换执行指令
                  execvp(g_argv[0], g_argv);
              }
              else
              {
                  //父进程 -- 等待子进程,并获取子进程的退出信息
                  int status = 0;
                  pid_t rid = waitpid(id, &status, 0);
                  if(rid > 0) // 等待成功
                  {
                      //将退出信息存入全局exit_code
                      exit_code = WEXITSTATUS(status);
                  }
              }
          }
          
          int main()
          {
              while(true)
              {
                  //打印命令行提示符
                  PrintCommandPrompt();
          
                  //读入命令
                  char command[1024];
                  if(!GetCommandLine(command, sizeof command)) // 若读取错误,就continue重新读取
                  {
                      std::cout << "输入错误" << std::endl;
                      continue;
                  }
                  // //打印输入的命令行
                  // std::cout << command << std::endl;
          
                  //命令行解析
                  if(!CommandParse(command))
                  {
                      std::cout << "命令行解析失败" << std::endl;
                      continue;
                  }
                  // //打印解析结果
                  // for(int i = 0; i < g_argc; i++)
                  // {
                  //     std::cout << g_argv[i] << std::endl;
                  // }
          
                  //内建命令的处理
                  if(CheckAndExecBuiltin())
                  {
                      continue; // 是内建命令,执行完毕就回去重新打印提示符
                  }
          
                  //不是内建命令,由子进程处理
                  Execute();
              }
              return 0;
          }
          

          接下来,我们输入一些指令进行测试,看看我们实现的shell能否达到预期效果:

          ![[Pasted image 20250518203838.png]]

          perfect!

          自实现shell的全部代码如下:

          #include <iostream>
          #include <cstdio>
          #include <cstdlib>
          #include <cstring>
          #include <unistd.h>
          #include <sys/types.h>
          #include <sys/wait.h>
          
          //全局变量存储命令行参数及其个数
          int g_argc = 0;
          char* g_argv[128];
          
          //全局的当前工作路径
          char cwd[256];
          
          //全局变量记录上一个程序的退出码
          int exit_code;
          
          //获取当前用户名
          const char* GetUserName()
          {
              const char* name = getenv("USER");
              return name == nullptr ? "none" : name;
          }
          
          //获取当前主机名
          const char* GetHostName()
          {
              const char* name = getenv("HOSTNAME");
              return name == nullptr ? "none" : name;
          }
          
          //获取当前工作路径
          const char* GetPwd()
          {
              const char* pwd = getenv("PWD");
              return pwd == nullptr ? "none" : pwd;
          }
          
          //获取用户的家目录
          const char* GetHome()
          {
              const char* home = getenv("HOME");
              return home == nullptr ? "" : home;
          }
          
          //创建并打印命令行提示符
          void PrintCommandPrompt()
          {
              char CommandPrompt[1024];
              
              //处理当前工作路径
              std::string pwd = GetPwd();
              if(pwd != "/") // 如果是根目录,则直接输出
                  pwd = pwd.substr(pwd.rfind('/') + 1); // 查找最后一个"/",并从下一个位置开始分割
          
              //这里为了区分Bash,用大括号
              snprintf(CommandPrompt, sizeof CommandPrompt,"{%s@%s %s}@ " , GetUserName(), GetHostName(), pwd.c_str());
              std::cout << CommandPrompt << std::flush; // 打印并刷新缓冲区
          }
          
          //获取用户输入的命令行
          bool GetCommandLine(char* command, int size)
          {
              //从键盘读取命令
              if(fgets(command, size, stdin) == nullptr)
              {
                  return false;
              }
          
              //注意清理末尾的'n'
              if(strlen(command) == 0) return false;
              command[strlen(command) - 1] = '';
              return true;
          }
          
          //命令行解析
          bool CommandParse(char* command)
          {
              g_argc = 0;
              for(char* p = strtok(command, " "); p != nullptr; p = strtok(nullptr, " "))
              {
                  g_argv[g_argc++] = p;
              }
              return g_argc == 0 ? false : true;
          }
          
          void Cd()
          {
              std::string dst;
              if(g_argc == 1 || g_argv[1] == std::string("~")) // 处理进入家目录的情况
              {
                  dst = GetHome();
                  if(dst == "") return;
              }
              else
              {
                  dst = g_argv[1];
              }
              chdir(dst.c_str());
          
              //同时修改环境变量中的当前工作路径
              char tmp[128];
              if(getcwd(tmp, sizeof tmp))
              {
                  snprintf(cwd, sizeof cwd, "PWD=%s", tmp);
                  putenv(cwd);
              }
          }
          
          void Echo()
          {
              std::string op = g_argv[1];
              if(op == "$?") // 打印上一个程序的退出码
              {
                  std::cout << exit_code << std::endl;
                  exit_code = 0;
              }
              else if(op[0] == '$') // 打印环境变量的值
              {
                  //从"$"后的第一个字符开始,获取环境变量名
                  std::string name = op.substr(1);
          
                  //用getenv获取环境变量值
                  const char* value = getenv(name.c_str());
                  if(value)
                      std::cout << value << std::endl;
              }
              else // 打印字符串
              {
                  std::cout << op << std::endl;
              }
          }
          
          //内建命令处理
          bool CheckAndExecBuiltin()
          {
              //取出命令行参数表的首元素,判断是否为内建命令,如果是,则直接执行
              std::string str = g_argv[0];
              if(str == "cd")
              {
                  Cd();
                  return true;
              }
              else if(str == "echo")
              {
                  Echo();
                  return true;
              }
              //else if(...)
              return false; // 不是内建命令,直接返回
          }
          
          //执行非内建命令
          void Execute()
          {
              //创建子进程
              pid_t id = fork();
              if(id < 0)
              {
                  //创建子进程失败,直接退出
                  exit(1);
              }
              else if(id == 0)
              {
                  //子进程 -- 通过程序替换执行指令
                  execvp(g_argv[0], g_argv);
              }
              else
              {
                  //父进程 -- 等待子进程,并获取子进程的退出信息
                  int status = 0;
                  pid_t rid = waitpid(id, &status, 0);
                  if(rid > 0) // 等待成功
                  {
                      //将退出信息存入全局exit_code
                      exit_code = WEXITSTATUS(status);
                  }
              }
          }
          
          int main()
          {
              while(true)
              {
                  //打印命令行提示符
                  PrintCommandPrompt();
          
                  //读入命令
                  char command[1024];
                  if(!GetCommandLine(command, sizeof command)) // 若读取错误,就continue重新读取
                  {
                      std::cout << "输入错误" << std::endl;
                      continue;
                  }
                  // //打印输入的命令行
                  // std::cout << command << std::endl;
          
                  //命令行解析
                  if(!CommandParse(command))
                  {
                      std::cout << "命令行解析失败" << std::endl;
                      continue;
                  }
                  // //打印解析结果
                  // for(int i = 0; i < g_argc; i++)
                  // {
                  //     std::cout << g_argv[i] << std::endl;
                  // }
          
                  //内建命令的处理
                  if(CheckAndExecBuiltin())
                  {
                      continue; // 是内建命令,执行完毕就回去重新打印提示符
                  }
          
                  //不是内建命令,由子进程处理
                  Execute();
              }
              return 0;
          }
          

          本篇文章,我们基于之前学习的Linux进程相关概念以及进程控制接口,实现了一个简易版的shell。本次实现让我们对Shell的运行原理有了更深刻的理解。

          以上就是Linux实现简易版Shell的代码详解的详细内容,更多关于Linux简易版Shell的资料请关注风君子博客其它相关文章!

          您可能感兴趣的文章:

          • 分享个简易版Linux服务器初始化Shell脚本
          • Linux实现自主Shell命令行解释器
          • Linux下Shell编程之sed命令的用法详解
          • Linux上读取或设置环境和shell变量的教程

          站内搜索