世界网站排名,手机我wordpress,学校网站建设的成果,手机网站加百度商桥文章目录1.打印命令提示符2.获取用户输入指令3.重定向分析4.命令行参数表,环境变量表,初始化5.命令解析6.命令执行6.1.创建子进程6.2 处理内建命令6.3 文件重定向7.源码前言
在实现shell的时候我们先创建自己myshell目录#xff0c;在目录中创建myshell.cc文件#xff0c;因…
文章目录1.打印命令提示符2.获取用户输入指令3.重定向分析4.命令行参数表,环境变量表,初始化5.命令解析6.命令执行6.1.创建子进程6.2 处理内建命令6.3 文件重定向7.源码前言
在实现shell的时候我们先创建自己myshell目录在目录中创建myshell.cc文件因为shell本来是用c语言写的但为了方便我们这里使用c和c混编。
首先我们做一个整体框架
int main()
{//shell启动的时候从系统获得环境变量//我们的环境变量信息应该统一从父shell取InitEnv();while(1){//1.打印提示信息//printf([%s%s %s]#, GetUserName(),GetHostName(),GetPwd());PrintCommandPrompt();//2.获取命令行提示符char commandline[COMMAND_SIZE];if(!GetCommandLine(commandline,sizeof(commandline))){continue;}//3.重定向分析 ls -a file.txt - ls -a file.txt// 判断重定向的方向// 输出 拼接输出 输入RedirCheck(commandline);//printf(redir %d filename %s\n, redir, filename.c_str());//4.命令行分析if(!CommandParse(commandline)){continue;}//PrintArgv();//5.检测是否是内键命令if(CheckAndExecBuiltin()){continue;}//6.执行命令Execute();}//清理工作clear();return 0;
}1.打印命令提示符
首先我们需要给用户显示提示信息就像我们在使用shell时所看到的提示信息一样如下
对它进行分析 所以我们可以这样定义宏,一个是方便打印的一个是命令长度
#define FORMAT [%s%s %s]#
#define COMMAND_SIZE 1024对于用户名主机号我们可以通过getenv从环境变量中得到但是获取当前路径我们不能使用getenv因为我们myshell的环境变量使用的是父进程的环境变量我们在当前使用cd命令切换路径环境变量中的pwd并不会有改变。
所以获取当前路径我们可以使用getcwd直接查询操作系统的文件系统获取当前进程的工作目录的绝对路径。而不用依赖环境变量。当然我们最好单独设计一个GetPwd把它封装起来这样也方便把当前路径加入到环境变量中。如下
//for test
char cwd[1024];
char cwdenv[1024];//last exit code
int lastcode 0;const char* GetPwd()
{//const char* pwd getenv(PWD);const char* pwd getcwd(cwd,sizeof(cwd));if(pwd ! NULL){snprintf(cwdenv,sizeof(cwdenv),PWD%s,pwd);putenv(cwdenv);}return pwd NULL ? None : pwd;
}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* GetHome()
{const char* home getenv(HOME);return home NULL ? : home;
}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,[%s%s %s]#,GetUserName(),GetHostName(),GetPwd());//snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());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);
}2.获取用户输入指令
获取用户输入因为用户输入的命令行参数是一个字符串中间含有空格。所以我们不用scanfcin进行输入。这里我们使用fgets。
bool GetCommandLine(char* out,int size)
{ char* c fgets(out, size,stdin);if(c nullptr){return false; }//去掉\nc[strlen(out) -1] 0;if(strlen(c) 0){return false;}return true;
}3.重定向分析
在用户输入的指令中可能含有重定向操作所以我们要提前特殊处理一下字符串并把它做一个分割。
既然是重定向也就是我们打开需要重向到的那个文件所以我们需要获取打开方式和文件名。
重定向有三种 输入重定向以读的方式打开文件输出重定向以写的方式打开文件追加重定向以追加的方式打开文件所以我们可以使用宏来标记这些情况。
//3.关于重定向的内容
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUR_REDIR 2
#define APPEND_REDIR 3int redir NONE_REDIR;
string filename;void TrimSpace(char cmd[],int end)
{while(isspace(cmd[end])){end;}
}void RedirCheck(char cmd[])
{redir NONE_REDIR; filename.clear();int start 0;int end strlen(cmd) - 1;//ls -a -l file.txt // while(end start){if(cmd[end] ){cmd[end] 0;TrimSpace(cmd,end);redir INPUT_REDIR;filename cmd end;break;}else if(cmd[end] ){//if(cmd[end - 1] ){cmd[end-1] 0;redir APPEND_REDIR;}//else{redir OUTPUR_REDIR;}cmd[end] 0;TrimSpace(cmd,end);filename cmd end;break;}else{end--;}}}4.命令行参数表,环境变量表,初始化
在shell中有两张表命令行参数表和环境变量表实质都是字符串数组。
命令行参数表用来储存用户输入的命令行参数。环境变量表用来储存当前进程的属性和状态。
所以我们可以这样做一个全局变量
//shell自定义的全局变量
//1.命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc 0;//2.环境变量表
#define MAXENVS 100
char* g_env[MAXENVS];
int g_envs 0;环境变量表需要我们在程序启动时就将它导入 当然程序启动后环境变量默认是父进程的所以我们可以重新开辟空间把原环境变量的数据拷贝过来然后再把environ更新为新的地址。具体实现请参考下文源码。
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;}//for testg_env[g_envs] (char*)MIHAYOU666; g_env[g_envs] NULL; //导成环境变量for(int i 0;g_env[i];i){putenv(g_env[i]);}environ g_env;
}5.命令解析
刚才我们获取到了用户的输入得到一个字符串需要把它一个一个按空格分开来得到一张命令行参数表。方便后面做进程替换。
//命令行分析 ls -a -l - ls -a -l
bool CommandParse(char* commandline)
{
#define SEP g_argc 0;g_argv[g_argc] strtok(commandline,SEP);while((bool)(g_argv[g_argc] strtok(nullptr,SEP)));//会多一个减掉g_argc--;return g_argc 0 ? true : false;
}6.命令执行
6.1.创建子进程
shell执行命令的实质就是进程替换我们在做进程替换的时候不想结束父进程那么需要我们创建一个新的子进程让子进程来做替换。
6.2 处理内建命令
有一些命令比如cd是一个内建命令子进程是无法完成的需要系统来执行。我们可以使用chdir来完成,chdir函数声明如下
bool Cd()
{if(g_argc 1){string home GetHome();if(home.empty()){return true;}chdir(home.c_str());}else{string where g_argv[1];chdir(where.c_str());}return true;
}它的作用是进入某个目录需要传一个目录的路径。
这里也支持echo的一些操作
void Echo()
{if(g_argc 2){//echo hello//echo $?//echo $PATHstring opt g_argv[1];if(opt $?){cout lastcode endl;}else if(opt[0] $){string env_name opt.substr(1);const char* env_value getenv(env_name.c_str());if(env_value){cout env_value endl;}}else{cout opt endl;}}
}bool CheckAndExecBuiltin()
{string cmd g_argv[0];if(cmd cd){Cd();return true;}else if(cmd echo){Echo();return true;}return false;
}6.3 文件重定向
pcd文件数组储存了这个pcb打开的所有文件信息地址。文件描述符记fdpcb的文件数组中的一个下标0下标的文件为标准输入流1下标的文件为标准输出流2下标的文件为标准错误流这三个都是系统默认打开的文件。重定向系统在对文件进行操作时只认fd所以重定向的实质就是一个fd位置的信息被其他fd的信息覆盖。 指的都是从原来的标准输出fd1重定向到某个文件 从原来的标准输入fd0重定向到某个文件。所以这里我们只需要打开新的文件并获取到它的fd然后使用dup2把新文件的地址信息覆盖到fd1的文件上就行。然后关闭新文件的fd。
int Execute()
{pid_t id fork();if(id 0){int fd 0;//子进程检测重定向if(redir INPUT_REDIR){fd open(filename.c_str(), O_RDONLY);if(fd 0){exit(1);}dup2(fd,0);close(fd);}else if(redir OUTPUR_REDIR){fd open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC,0666);if(fd 0){exit(2);}dup2(fd,1);close(fd);}else if(redir APPEND_REDIR){fd open(filename.c_str(), O_CREAT | O_WRONLY |O_APPEND,0666);if(fd 0){exit(3);}dup2(fd,1);close(fd);}else{}//进程替换不会改变重定向的内容//子进程执行execvp(g_argv[0],g_argv);//程序替换后面的程序就不执行了exit(1);}//父进程等待int status 0;pid_t rid waitpid(id,status,0);////取消报警//(void)rid;if(rid 0){lastcode WEXITSTATUS(status);}return 0;
}7.源码
#include iostream
#include cstdio
#include cstdlib
#include cstring
#include unistd.h
#include sys/types.h
#include sys/wait.h
#include string
#include sys/stat.h
#include fcntl.h
using namespace std;#define COMMAND_SIZE 1024
#define FORMAT [%s%s %s]#//shell自定义的全局变量
//1.命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc 0;//2.环境变量表
#define MAXENVS 100
char* g_env[MAXENVS];
int g_envs 0;//for test
char cwd[1024];
char cwdenv[1024];//last exit code
int lastcode 0;//3.关于重定向的内容
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUR_REDIR 2
#define APPEND_REDIR 3int redir NONE_REDIR;
string filename;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 getenv(PWD);const char* pwd getcwd(cwd,sizeof(cwd));if(pwd ! NULL){snprintf(cwdenv,sizeof(cwdenv),PWD%s,pwd);putenv(cwdenv);}return pwd NULL ? None : pwd;
}const char* GetHome()
{const char* home getenv(HOME);return home NULL ? : home;
}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,[%s%s %s]#,GetUserName(),GetHostName(),GetPwd());//snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());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)
{ char* c fgets(out, size,stdin);if(c nullptr){return false; }//去掉\nc[strlen(out) -1] 0;if(strlen(c) 0){return false;}return true;
}//命令行分析 ls -a -l - ls -a -l
bool CommandParse(char* commandline)
{
#define SEP g_argc 0;g_argv[g_argc] strtok(commandline,SEP);while((bool)(g_argv[g_argc] strtok(nullptr,SEP)));//会多一个减掉g_argc--;return g_argc 0 ? true : false;
}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;}//for testg_env[g_envs] (char*)MIHAYOU666; 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){string home GetHome();if(home.empty()){return true;}chdir(home.c_str());}else{string where g_argv[1];chdir(where.c_str());}return true;
}void Echo()
{if(g_argc 2){//echo hello//echo $?//echo $PATHstring opt g_argv[1];if(opt $?){cout lastcode endl;}else if(opt[0] $){string env_name opt.substr(1);const char* env_value getenv(env_name.c_str());if(env_value){cout env_value endl;}}else{cout opt endl;}}
}bool CheckAndExecBuiltin()
{string cmd g_argv[0];if(cmd cd){Cd();return true;}else if(cmd echo){Echo();return true;}return false;
}void PrintArgv()
{for(int i 0;g_argv[i];i){printf(argv[%d]-%s\n, i,g_argv[i]);}printf(size %d\n, g_argc);
}void clear()
{for(int i 0;g_env[i];i){free(g_env[i]);g_env[i] NULL;}
}int Execute()
{pid_t id fork();if(id 0){int fd 0;//子进程检测重定向if(redir INPUT_REDIR){fd open(filename.c_str(), O_RDONLY);if(fd 0){exit(1);}dup2(fd,0);close(fd);}else if(redir OUTPUR_REDIR){fd open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC,0666);if(fd 0){exit(2);}dup2(fd,1);close(fd);}else if(redir APPEND_REDIR){fd open(filename.c_str(), O_CREAT | O_WRONLY |O_APPEND,0666);if(fd 0){exit(3);}dup2(fd,1);close(fd);}else{}//进程替换不会改变重定向的内容//子进程执行execvp(g_argv[0],g_argv);//程序替换后面的程序就不执行了exit(1);}//父进程等待int status 0;pid_t rid waitpid(id,status,0);////取消报警//(void)rid;if(rid 0){lastcode WEXITSTATUS(status);}return 0;
}void TrimSpace(char cmd[],int end)
{while(isspace(cmd[end])){end;}
}void RedirCheck(char cmd[])
{redir NONE_REDIR; filename.clear();int start 0;int end strlen(cmd) - 1;//ls -a -l file.txt // while(end start){if(cmd[end] ){cmd[end] 0;TrimSpace(cmd,end);redir INPUT_REDIR;filename cmd end;break;}else if(cmd[end] ){//if(cmd[end - 1] ){cmd[end-1] 0;redir APPEND_REDIR;}//else{redir OUTPUR_REDIR;}cmd[end] 0;TrimSpace(cmd,end);filename cmd end;break;}else{end--;}}}int main()
{//shell启动的时候从系统获得环境变量//我们的环境变量信息应该统一从父shell取InitEnv();while(1){//1.打印提示信息//printf([%s%s %s]#, GetUserName(),GetHostName(),GetPwd());PrintCommandPrompt();//2.获取命令行提示符char commandline[COMMAND_SIZE];if(!GetCommandLine(commandline,sizeof(commandline))){continue;}//3.重定向分析 ls -a file.txt - ls -a file.txt// 判断重定向的方向// 输出 拼接输出 输入RedirCheck(commandline);//printf(redir %d filename %s\n, redir, filename.c_str());//4.命令行分析if(!CommandParse(commandline)){continue;}//PrintArgv();//5.检测是否是内键命令if(CheckAndExecBuiltin()){continue;}//6.执行命令Execute();}//清理工作clear();return 0;
}