目录
一、基本功能实现
二、目前出现的问题
三、解决方法
一、基本功能实现
已知shell是个死循环 所以main函数中是一个while的死循环,然后我们需要一张gargv的命令行参数表,后面打印出自己的用户名 主机名和当前路径 从环境变量中获取即可!!!
解析字符串用的是strtok自行去了解这个函数的用法!!!!
执行命令用的是通过fork子进程去执行我们需要执行的命令,在shell中我们使用命令都是通过创建进程去执行的,所以我们fork出子进程+execvp去执行程序!!!!
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <sys/types.h> #define MAXSIZE 128 char *gargv[MAXSIZE]; int gargc = 0; const char *GetUserName() { char *name = getenv("USER"); if (name == NULL) return "none"; return name; } const char *GetHostName() { static char hostname[256] = {0}; if (gethostname(hostname, sizeof(hostname) - 1) != 0) return "none"; return hostname; } // const char* GetHostName() // { // char *hostname = getenv("HOSTNAME"); // if (hostname == NULL) // return "none"; // return hostname; // } char* GetPwd() { char *pwd = getenv("PWD"); if (pwd == NULL) return "none"; return pwd; } void PrintCommandLine() { // 用户名 @主机名 当前路径 printf("[%s@%s %s]# ", GetUserName(), GetHostName(), GetPwd()); fflush(stdout); } int GetCommand(char commandline[], int size) { if (NULL == fgets(commandline, size, stdin)) { return 0; } // 用户在输入的时候,至少会有一个回车 commandline[strlen(commandline) - 1] = '\0'; return strlen(commandline); } int ParseCommand(char* command_line) { gargc = 0; memset(gargv, 0, sizeof(gargv)); gargv[0] = strtok(command_line, " "); while(gargv[++gargc] = strtok(NULL, " ")); // printf("gargc: %d\n", gargc); // for(int i = 0; gargv[i]; i++) // { // printf("gargv[%d]: %s\n", i, gargv[i]); // } return gargc; } int ExecuteCommand() { pid_t id = fork(); if(id < 0) return -1; else if(id == 0) { // child 如何执行命令??? execvp 这个就行 execvp(gargv[0], gargv); exit(0); } else { int status = 0; pid_t rid = waitpid(id, &status, 0); if(rid > 0) { // printf("wait success!\n"); } } return 0; } int main() { char command_line[MAXSIZE] = {0}; while (1) { // 1. 打印命令行字符串 PrintCommandLine(); // 2 获取用户输入 if(0 == GetCommand(command_line, sizeof(command_line))) { continue; } // printf("%s\n", command_line); // 3 解析字符串 ParseCommand(command_line); // 4 执行这个命令 ExecuteCommand(); } }二、目前出现的问题
cd.. : 路径切换,切换的是谁的路径???->> 当前进程---父进程bash!!!
执行命令之后bash的子进程,所有的子进程都会进程父进程当前的工作路径!!!
更改bash的工作路径,就是更改了后续执行所有指令(进程)的工作路径!!!
子进程执行cd 就是fork之后子进程去exec函数执行cd 替换!!!执行完,更改就退了父进程也看不到!!!!
三、解决方法
我们中间需要加一步,这个命令是需要子进程执行还是父进程执行??
父进程执行的就是内建命令!!!
父进程使用chdir变成新的路径,但是环境变量env没改!!!所以我们可以用getcwd来获取
char* GetPwd() { // char *pwd = getenv("PWD"); char* pwd = getcwd(cwd, sizeof(cwd)); if (pwd == NULL) return "none"; return pwd; }也可以通过putenv去改变环境变量来实现!!!同时我们截取了最后一个的文件路径如下
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <sys/types.h> #include <string> #include <iostream> #define MAXSIZE 128 char *gargv[MAXSIZE]; int gargc = 0; char cwd[1028]; static std::string rfindDir(const std:: string& p) { if(p == "/") return p; const std::string psep = "/"; auto pos = p.rfind(psep); if(pos == std::string::npos) { return std::string(); } return p.substr(pos+1); } const char *GetUserName() { char *name = getenv("USER"); if (name == NULL) return "none"; return name; } const char *GetHostName() { static char hostname[256] = {0}; if (gethostname(hostname, sizeof(hostname) - 1) != 0) return "none"; return hostname; } // const char* GetHostName() // { // char *hostname = getenv("HOSTNAME"); // if (hostname == NULL) // return "none"; // return hostname; // } const char* GetPwd() { char *pwd = getenv("PWD"); // char* pwd = getcwd(cwd, sizeof(cwd)); if (pwd == NULL) return "none"; return pwd; } void PrintCommandLine() { // 用户名 @主机名 当前路径 printf("[%s@%s %s]# ", GetUserName(), GetHostName(), rfindDir(GetPwd()).c_str()); fflush(stdout); } int GetCommand(char commandline[], int size) { if (NULL == fgets(commandline, size, stdin)) { return 0; } // 用户在输入的时候,至少会有一个回车 commandline[strlen(commandline) - 1] = '\0'; return strlen(commandline); } int ParseCommand(char* command_line) { gargc = 0; memset(gargv, 0, sizeof(gargv)); gargv[0] = strtok(command_line, " "); while(gargv[++gargc] = strtok(NULL, " ")); // printf("gargc: %d\n", gargc); // for(int i = 0; gargv[i]; i++) // { // printf("gargv[%d]: %s\n", i, gargv[i]); // } return gargc; } int ExecuteCommand() { pid_t id = fork(); if(id < 0) return -1; else if(id == 0) { // child 如何执行命令??? execvp 这个就行 execvp(gargv[0], gargv); exit(0); } else { int status = 0; pid_t rid = waitpid(id, &status, 0); if(rid > 0) { // printf("wait success!\n"); } } return 0; } // return val: // 0: 不是内建命令 1:是内建命令 如执行完毕 int CheckBuiltinExecute() { if(strcmp(gargv[0], "cd") == 0) { // 内建命令!! if(gargc == 2) { // 更改进程内核中的路径 chdir(gargv[1]); // 更改环境变量!!! char pwd[1024]; getcwd(pwd, sizeof(pwd)); snprintf(cwd, sizeof(cwd), "PWD=%s", pwd); putenv(cwd); } return 1; } return 0; } int main() { char command_line[MAXSIZE] = {0}; while (1) { // 1. 打印命令行字符串 PrintCommandLine(); // 2 获取用户输入 if(0 == GetCommand(command_line, sizeof(command_line))) { continue; } // printf("%s\n", command_line); // 3 解析字符串 ParseCommand(command_line); // 这个命令是需要自己执行 还是让子进程执行? if(CheckBuiltinExecute()) { continue; } // 4 让子进程执行! ExecuteCommand(); } }1. 后面我们增加了一个env环境变量表!
2. 同时设置了退出码lastcode
3. 同时写了echo的分之 如果echo $?(退出码) echo $PATH 和正常echo!
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <sys/types.h> #include <string> #include <iostream> #define MAXSIZE 128 char *gargv[MAXSIZE]; int gargc = 0; // 最近一个命令的退出码 int lastcode = 0; // 环境变量 char* genv[MAXSIZE]; int genvc = 0; char cwd[1028]; void LoadEnv() { // 从配置文件中来的 正常情况 // 我们这是从父进程中拷贝 for(; environ[genvc]; genvc++) { genv[genvc] = (char*)malloc(sizeof(char)*4096); strcpy(genv[genvc], environ[genvc]); } genv[genvc] = NULL; printf("Load env: \n"); for(int i = 0; genv[i];i++) { printf("genv[%d]: %s\n", i, genv[i]); } } static std::string rfindDir(const std:: string& p) { if(p == "/") return p; const std::string psep = "/"; auto pos = p.rfind(psep); if(pos == std::string::npos) { return std::string(); } return p.substr(pos+1); } const char *GetUserName() { char *name = getenv("USER"); if (name == NULL) return "none"; return name; } const char *GetHostName() { static char hostname[256] = {0}; if (gethostname(hostname, sizeof(hostname) - 1) != 0) return "none"; return hostname; } // const char* GetHostName() // { // char *hostname = getenv("HOSTNAME"); // if (hostname == NULL) // return "none"; // return hostname; // } const char* GetPwd() { char *pwd = getenv("PWD"); // char* pwd = getcwd(cwd, sizeof(cwd)); if (pwd == NULL) return "none"; return pwd; } void PrintCommandLine() { // 用户名 @主机名 当前路径 printf("[%s@%s %s]# ", GetUserName(), GetHostName(), rfindDir(GetPwd()).c_str()); fflush(stdout); } int GetCommand(char commandline[], int size) { if (NULL == fgets(commandline, size, stdin)) { return 0; } // 用户在输入的时候,至少会有一个回车 commandline[strlen(commandline) - 1] = '\0'; return strlen(commandline); } int ParseCommand(char* command_line) { gargc = 0; memset(gargv, 0, sizeof(gargv)); gargv[0] = strtok(command_line, " "); while(gargv[++gargc] = strtok(NULL, " ")); // printf("gargc: %d\n", gargc); // for(int i = 0; gargv[i]; i++) // { // printf("gargv[%d]: %s\n", i, gargv[i]); // } return gargc; } int ExecuteCommand() { pid_t id = fork(); if(id < 0) return -1; else if(id == 0) { // child 如何执行命令??? execvp 这个就行 execvp(gargv[0], gargv); exit(0); } else { int status = 0; pid_t rid = waitpid(id, &status, 0); if(rid > 0) { // printf("wait success!\n"); // 退出码 lastcode = WEXITSTATUS(status); } } return 0; } // return val: // 0: 不是内建命令 1:是内建命令 如执行完毕 int CheckBuiltinExecute() { if(strcmp(gargv[0], "cd") == 0) { // 内建命令!! if(gargc == 2) { // 更改进程内核中的路径 chdir(gargv[1]); // 更改环境变量!!! char pwd[1024]; getcwd(pwd, sizeof(pwd)); snprintf(cwd, sizeof(cwd), "PWD=%s", pwd); putenv(cwd); lastcode = 0; } return 1; } else if(strcmp(gargv[0], "echo") == 0) { if(gargc == 2) { if(gargv[1][0] == '$') { if(strcmp(gargv[1]+1, "?") == 0) { printf("lastcode: %d\n", lastcode); } else if(strcmp((gargv[1]+1), "PATH")==0) { printf("haha我是env:%s\n", getenv("PATH")); } lastcode = 0; return 1; } } } return 0; } int main() { // 0 从配置文件中获取环境变量填充环境变量表 LoadEnv(); char command_line[MAXSIZE] = {0}; while (1) { // 1. 打印命令行字符串 PrintCommandLine(); // 2 获取用户输入 if(0 == GetCommand(command_line, sizeof(command_line))) { continue; } // printf("%s\n", command_line); // 3 解析字符串 ParseCommand(command_line); // 这个命令是需要自己执行 还是让子进程执行? if(CheckBuiltinExecute()) { continue; } // 4 让子进程执行! ExecuteCommand(); } }