Ai
1 Star 0 Fork 0

Miun11/Linux_learning

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
myshell.cpp 7.57 KB
一键复制 编辑 原始数据 按行查看 历史
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<map>
#include<unordered_map>
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
// 命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC]; // 命令行参数表
int g_argc = 0; // 实际输入的命令行参数个数
// 环境变量表
#define MAXENVS 128
char *g_env[MAXENVS]; // 环境变量表
int g_envs = 0; // 实际环境变量个数
// 别名映射表
unordered_map<string, string> alias_list;
void InitEnv()
{
// 本来要从配置文件生成环境变量表(实现不了)
// 通过environ获得,environ是一个全局外部二级指针,核心作用是指向当前进程的环境变量表(以 NULL 结尾的字符串数组)
// 我们从shell启动时获得,即当前进程为shell进程
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]); // 将shell的环境变量表内容向子进程环境变量表拷贝
g_envs++; // 环境变量个数
}
// 环境变量表最后放置NULL
g_env[g_envs++] = (char*)"MY_ENV_FLAG=HDS";
g_env[g_envs++] = NULL;
// 导成环境变量表
for (int i = 0; g_env[i]; i++)
{
putenv(g_env[i]);
}
environ = g_env; // 更新子进程environ全局变量
}
// 获取用户名
const char *getUSERNAME()
{
char *username = getenv("USER");
return username == NULL ? NULL : username;
}
// 获取主机名
const char *getHOSTNAME()
{
const char *hostname = getenv("HOSTNAME");
return hostname == NULL ? "None" : hostname;
}
// 获取当前工作路径
// 定义全局变量
char cwd[1024]; // 存储当前工作路径
char cwdenv[1024]; // 存储环境变量(当前工作路径切换后需要修改环境变量的值)
const char *getPWD()
{
const char* pwd = getcwd(cwd,sizeof(cwd));
if(pwd != NULL)
{
snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
putenv(cwdenv); // 修改环境变量(覆盖式修改),保证环境变量值是最新的(cd 可能会切换路径)
}
return pwd == NULL ? "None" : pwd;
}
// 获取家目录
const char* GetHome()
{
const char* home = getenv("HOME");
return home == NULL ? "" : home;
}
// 获取当前路径的最后部分(当前目录名)
#define SLASH "/" // 路径分隔符
string get_dirPWD(const char *pwd)
{
string path = pwd;
if(path == SLASH) // 当当前路径为家目录
return SLASH;
size_t pos = path.rfind(SLASH);
if(pos == string::npos) // 没找到
return "BUG";
return path.substr(pos + 1);
}
// 制作命令行提示符
void Make_Commandprompt(char *prompt, int size)
{
snprintf(prompt, size, FORMAT, getUSERNAME(), getHOSTNAME(), get_dirPWD(getPWD()).c_str()); // 在命令行中仅打印当前路径的最后部分
//snprintf(prompt, size, FORMAT, getUSERNAME(), getHOSTNAME(), getPWD()); // 在命令行中打印出完整当前路径
}
// 打印命令行提示符
void Print_Commandprompt()
{
char prompt[COMMAND_SIZE]; // 创建命令行字符数组
Make_Commandprompt(prompt, sizeof(prompt)); // 制作命令行
printf("%s" ,prompt);
fflush(stdout);
}
// 获得命令行(等待用户输入)
bool Get_Commandline(char *commandline, int size)
{
// ls -a -l, fgets写入commandline数组后自动在结尾添加'\0',即"ls -a -l\0"
char *line = fgets(commandline, size, stdin);
if(line == NULL) // 写入失败
return false;
// alias ll=ls -l
// commandline = {"ll"}, alias_list = {ll, ls -l}
size_t len = strlen(commandline);
commandline[len - 1] = 0; // 清理\n
if(len == 0) return false; // 没有指令
auto it = alias_list.find(line);
if(it != alias_list.end())
{
string value_name = it->second;
size_t size = value_name.size();
for(size_t i = 0; i < size; i++)
{
commandline[i] = value_name[i];
printf("%c", value_name[i]);
}
cout << endl;
}
return true;
// printf("echo %s",commandline);
// fflush(stdout);
}
// 命令行分析,分隔命令行,方便执行选项
#define DIV " " // 分隔符
bool Commandline_Prase(char *commandline)
{
g_argc = 0;
g_argv[g_argc++] = strtok(commandline, DIV); // 用strtok()函数分割命令行
while((bool)(g_argv[g_argc++] = strtok(nullptr, DIV))); // 继续向后分割需要传nullptr作为第一个参数
g_argc--;
return true;
}
// 内建命令cd
bool Cd()
{
// cd 命令:cd, cd .., cd -, cd ~, cd /, cd **
if(g_argc == 1) // cd:切换到家目录
{
string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
string where = g_argv[1];
if(where == "-")
{
// ...
}
else if(where == "~")
{
// 同cd
}
else
{
chdir(where.c_str());
}
}
return true;
}
// 内建命令 echo
int last_exit_code = 0; // 全局变量,存储上一个进程的退出码
void Echo()
{
// echo $?, echo $PATH, echo **
if(g_argc == 2)
{
string str = g_argv[1];
if(str == "$?")
{
cout << last_exit_code << endl;
last_exit_code = 0;
}
else if(str[0] == '$') // 打印环境变量值
{
string env_name = str.substr(1); // 获取环境变量名
const char *env_value = getenv(env_name.c_str()); // 获取环境变量值
if(env_value)
cout << env_value << endl;
}
else
{
cout << str << endl;
}
}
}
// 内建命令alias
void Alias()
{
// alias ll=ls -a -l
string cmd = g_argv[1];
auto pos = cmd.find("=");
string key(cmd.begin(), cmd.begin() + pos);
string value(cmd.begin() + pos + 1, cmd.end());
for(int i = 2; g_argv[i]; i++)
{
value += " ";
value += g_argv[i];
}
alias_list[key] = value;
}
// 判断是否是内建命令并执行
bool CheckBuildin()
{
string cmd = g_argv[0];
if(cmd == "cd")
{
Cd();
return true;
}
else if(cmd == "echo")
{
Echo();
return true;
}
else if(cmd == "alias")
{
Alias();
return true;
}
return false;
}
// 打印命令行参数表
void Print_argv()
{
for(int i = 0; i < g_argc; i++)
{
printf("%s ", g_argv[i]);
}
printf("\n%d\n" ,g_argc);
}
// 执行指令
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)
{
last_exit_code = WEXITSTATUS(status);
}
return 0;
}
int main()
{
// 环境变量表在启动shell时生成
InitEnv();
while(true)
{
// 1. 打印命令行提示符
Print_Commandprompt();
char commandline[COMMAND_SIZE];
bool flag = Get_Commandline(commandline, sizeof(commandline)); // 2.获取指令
if(!flag)
continue;
Commandline_Prase(commandline);// 3.命令行分析
// 4.判断是否是内建命令
if(CheckBuildin())
continue;
// Print_argv(); // for test
// 4.执行
Execute();
}
return 0;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/miun11/Linux_learning.git
git@gitee.com:miun11/Linux_learning.git
miun11
Linux_learning
Linux_learning
master

搜索帮助