当前位置: 首页 > news >正文

Linux:简单自定义shell

1.实现原理

考虑下⾯这个与shell典型的互动:

[root@localhost epoll]# ls
client.cpp readme.md server.cpp utility.h
[root@localhost epoll]# ps
PID TTY TIME CMD
3451 pts/0 00:00:00 bash
3514 pts/0 00:00:00 ps
⽤下图的时间轴来表⽰事件的发⽣次序。其中时间从左向右。shell由标识为sh的⽅块代表,它随着时间的流逝从左向右移动。shell从⽤⼾读⼊字符串"ls"。shell建⽴⼀个新的进程,然后在那个进程中运⾏ls程序并等待那个进程结束。

 

然后shell读取新的⼀⾏输⼊,建⽴⼀个新的进程,在这个进程中运⾏程序 并等待这个进程结束。
所以要写⼀个shell,需要循环以下过程:
  1. 获取命令⾏
  2. 解析命令⾏
  3. 建⽴⼀个⼦进程(fork)
  4. 替换⼦进程(execvp)
  5. ⽗进程等待⼦进程退出(wait)

2.实现代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]"//shell定义的全局数据//1.命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;//2.环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;char cwd[1024];
char cwdenv[2048];//last exit code
int lastcode = 0;const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}void InitEnv()
{extern char** environ;memset(g_env, 0, sizeof(g_env));g_envs = 0;//从配置文件中获取环境变量for (int i = 0; environ[i]; i++){g_env[i] = (char*)malloc((strlen(environ[i]) + 1));strcpy(g_env[i], environ[i]);g_envs++;}//测试g_env[g_envs++] = (char*)"HAHA=for_test";g_env[g_envs] = NULL;//导成环境变量for (int i = 0; g_env[i]; i++){putenv(g_env[i]);}environ = g_env;
}bool Cd()
{if (g_argc == 1){std::string home = GetHome();if (home.empty())return true;chdir(home.c_str());}else{std::string where = g_argv[1];// cd - / cd ~if (where == "-"){// Todu}else if (where == "~"){// Todu}else{chdir(where.c_str());}}return true;
}void Echo()
{if (g_argc == 2){std::string opt = g_argv[1];if (opt == "$?"){std::cout << lastcode << std::endl;lastcode = 0;}else if (opt[0] == '$'){std::string env_name = opt.substr(1);const char* env_value = getenv(env_name.c_str());if (env_value)std::cout << env_value << std::endl;}else{std::cout << opt << std::endl;}}
}std::string DirName(const char* pwd)
{
#define SLASH "/"std::string dir = pwd;if (dir == SLASH)return SLASH;auto pos = dir.rfind(SLASH);if (pos == std::string::npos)return "BUG?";return dir.substr(pos + 1);
}void MakeCommandLine(char cmd_prompt[], int size)
{snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}void PrintCommandPrompt()
{char prompt[COMMAND_SIZE];MakeCommandLine(prompt, sizeof(prompt));printf("%s", prompt);fflush(stdout);
}bool GetCommandLine(char* out, int size)
{//ls -a -l ->"ls -a -l"字符串char* c = fgets(out, size, stdin);if (c == NULL)return false;//fgets失败out[strlen(out) - 1] = 0;//清理\nif (strlen(out) == 0)return false;//如只输入\nreturn true;
}bool CommandParse(char* commandline)
{
#define SEP " "g_argc = 0;//命令行分析"ls -a -l"->"ls" "-a" "-l"  g_argv[g_argc++] = strtok(commandline, SEP);while ((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));g_argc--;return g_argc > 0 ? true : false;}bool CheckAndExecBuildin()
{std::string cmd = g_argv[0];if (cmd == "cd"){Cd();return true;}else if (cmd == "echo"){Echo();return true;}//else if(cmd == "export")//else if(cmd == "alias")//...return false;
}int Execute()
{pid_t id = fork();if (id == 0){execvp(g_argv[0], g_argv);exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);}return 0;
}int main()
{// shell 启动的时候,从系统中获取环境变量// 我们的环境变量信息应该从父shell统一来InitEnv();while (true){//1.输出命令行提示符PrintCommandPrompt();//2.输入用户输入的命令char commandline[COMMAND_SIZE];if (!GetCommandLine(commandline, sizeof(commandline)))continue;//3.命令行分析"ls -a -l"->"ls" "-a" "-l"if (!CommandParse(commandline))continue;//4.检测并处理内建命令if (CheckAndExecBuildin())continue;//5.执行命令Execute();}return 0;
}

相关文章:

  • 界面控件DevExpress WPF v25.1预览 - 支持Windows 11系统强调色
  • 【图像识别改名】如何批量识别多个图片的区域内容给图片改名,批量图片区域文字识别改名,基于WPF和腾讯OCR的实现方案和步骤
  • PLC互连全攻略:Profinet和EthernetIP实操演示
  • 极狐GitLab 项目功能和权限解读
  • GMS认证之 CTS Verifier认证新变化
  • 【前端】【业务逻辑】【面试】JSONP处理跨域原理与封装
  • Python 设计模式:回调模式
  • WebGis与WebGL是什么,两者之间的关系?
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(6):MCP传输层配置与使用
  • 基于LightGBM-TPE算法对交通事故严重程度的分析与可视化
  • java 设计模式 原型模式
  • 【安装neo4j-5.26.5社区版 完整过程】
  • Linux系统用户迁移到其它盘方法
  • “融合Python与机器学习的多光谱遥感技术:数据处理、智能分类及跨领域应用”​
  • 在Windows上安装Git
  • AiEditor v1.3.8 发布
  • LeetCode---整数反转
  • 每日算法-250422
  • JavaFX实战:从零到一实现一个功能丰富的“高级反应速度测试”游戏
  • IO流详解
  • 解密帛书两千年文化传承,《帛书传奇》央视今晚开播
  • 对话地铁读书人|企业公关吴丑丑:阅读中相遇又重逢
  • 中国驻日本大使馆发言人就日方涉靖国神社消极动向答记者问
  • 日媒:日本公明党党首将访华,并携带石破茂亲笔信
  • 2025年度“沪惠保”今日开售:保费维持129元/人,进一步扩增国内外特药种类
  • KZ队史首冠,透过春决看CF电竞张扬的生命力