Linux命令行解釋器的模擬實(shí)現(xiàn)過(guò)程
一、整體框架:
首先我們把這個(gè)myshell大致進(jìn)行框架展示出:
我們首先創(chuàng)建數(shù)組cl保存要輸入的字符串;而只要讀取失敗就要一直讀取故我們?cè)讷@取,命令行輸入的時(shí)候利用了while循環(huán);其次就是如果是內(nèi)建命令;我們就要直接父進(jìn)程執(zhí)行完;無(wú)需execute再讓子進(jìn)程執(zhí)行了。
先說(shuō)一下想法:這里可執(zhí)行程序,把它當(dāng)成真正shell的bash;大部分命令都是通過(guò)調(diào)用子進(jìn)程來(lái)程序替換完成;有些命令是內(nèi)建的,故需要自己完成;而首先這個(gè)程序會(huì)繼承原本bash的那張環(huán)境變量表;這里我們模擬實(shí)現(xiàn)一下真正的bash的那兩張表:也就是說(shuō)我們用數(shù)組,通過(guò)拷貝原bash的表,改變environ指針來(lái)維護(hù)我們的數(shù)組(也就是我們自己的可執(zhí)行程序要調(diào)用的那張環(huán)境變量表) :這里補(bǔ)充一點(diǎn):對(duì)于環(huán)境變量如果我們env命令:它是通過(guò)environ指針來(lái)進(jìn)行查找打印的;局部打印就不一定了。 后面我們具體實(shí)現(xiàn)的時(shí)候會(huì)有所體現(xiàn),之后我們道來(lái)。
然后下面就是一步步對(duì)這些拆開(kāi)的函數(shù)進(jìn)行實(shí)現(xiàn)了。
二、初始化myshell的環(huán)境變量表和命令行參數(shù)表:
這里我們自己開(kāi)了兩個(gè)數(shù)組來(lái)模擬這兩張表;也就是拷貝父bash的那兩種表拷貝過(guò)來(lái)(簡(jiǎn)單模擬一下)這倆張表的內(nèi)容就可以作為我們后面程序替換執(zhí)行命令要傳遞的參數(shù)等。
void initenv(){ memset(env, 0, sizeof(env)); envs= 0; for(int i=0;environ[i];i++){ env[i]=(char*)malloc(strlen(environ[i])+1);//這里模擬的也可以不開(kāi)空間,直接棧上 strcpy(env[i], environ[i]); envs++; } env[envs] = NULL; // for(int i = 0; env[i]; i++) // { // putenv(env[i]); // } environ = env;//用自己生成的env表 }
這里我們的命令行參數(shù)表暫時(shí)不需要填充,但是需要把環(huán)境變量表由bash那里拷貝過(guò)來(lái);并改變了environ指針指向,也就是說(shuō)等我們執(zhí)行env操作的時(shí)候它就會(huì)打印我們的這個(gè)env數(shù)組了;比如后序我們用putenv等命令的話,它就會(huì)通過(guò)environ指針對(duì)我們的這個(gè)數(shù)組進(jìn)行一些增加/覆蓋環(huán)境變量的操作了。
三、命令行提示行的打?。?/h2>
我們讓它格式輸出這樣的格式:
#define FT "my simulate shell:%s@%s %s# "http://snprintf的format最大值
首先我們對(duì)比一下真正的命令解釋器:
我們此刻需要替換掉%s的就是通過(guò)環(huán)境變量找到USER,HOSTHOME ,PWD了:
const char* getuser(){ const char*u=getenv("USER"); return u==NULL?"NONE":u; } const char* gethostname(){ const char*h=getenv("HOSTNAME"); return h==NULL?"NONE":h; } const char* getpwd(){ const char*p=getenv("PWD"); return p==NULL?"NONE":p; }
但是這樣我們會(huì)發(fā)現(xiàn)因?yàn)槲覀兙S護(hù)的這張環(huán)境變量表,未添加其他修改功能,這里需要我們手動(dòng)修改;這樣pwd就不會(huì)變了(當(dāng)換目錄的時(shí)候):因此我們手動(dòng)維護(hù)一下:
下面是我們要添加的全局變量(因?yàn)閷?dǎo)入environ維護(hù)的二維數(shù)組應(yīng)該是地址;故給它整成全局):
//這里獲得環(huán)境變量和其他上面不同;因?yàn)楫?dāng)我們通過(guò)chdir改變當(dāng)前目錄的時(shí)候它在環(huán)境變量中的記錄(真正的bash實(shí)現(xiàn)了)而我們沒(méi)有實(shí)現(xiàn),因此我們 //可以通過(guò)getcwd每次調(diào)完新的目錄開(kāi)始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時(shí)候變化 const char *getpwd(){ char *pwd = getcwd(cwd, sizeof(cwd)); if(pwd != NULL) { snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd); putenv(cwdenv);//put進(jìn)去的是環(huán)境變量的地址也就是一個(gè)指針指向的是那段量,因此指針要么是全局要么在堆上 } return pwd==NULL?"NONE":pwd; }
普及一下用到的getcwd:
參數(shù):放入的數(shù)組;最大字節(jié)數(shù);成功返回這段pwd失敗就是NULL。
這里我們?cè)僮鲆粋€(gè)小優(yōu)化;也就是把路徑變短一下就乘當(dāng)下目錄:
std::string convert(const char *pwd){ std::string s=pwd; auto pos=s.rfind('/'); if(pos==std::string::npos)return "error"; if(pos==s.size()-1)return "/"; else return s.substr(pos+1); }
因此我們從末尾給它分割了一下:最后調(diào)用它返回的string對(duì)象的c_str接口就好;
這里順便說(shuō)一下;因?yàn)楹竺婧芏喽加玫竭@一點(diǎn):就是經(jīng)常操作的時(shí)候把char串變成string然后調(diào)用它的c_str()為了方便;以及后面很多要注意作用域:因此考慮了全局設(shè)計(jì)。
再下面就是命令行打印了:
void ptcmdprompt(){ char p[CS]={0}; snprintf(p,sizeof(p),FT, getuser(),gethostname(),convert(getpwd()).c_str() ); printf("%s",p); //無(wú)\n及時(shí)刷新標(biāo)準(zhǔn)輸出緩沖區(qū) fflush(stdout); }
下面展示下效果:
四、獲取命令參數(shù):
這里邏輯比較簡(jiǎn)單就是把我們輸入的字符讀入然后把后面的\n去掉:
bool gtcmdline(char *p,int size){ char *j=fgets(p,size,stdin); // printf("參數(shù)是:%d",size); if(j==NULL)return false; //printf("%s\n",p); p[strlen(p)-1]=0;//干掉\n if(strlen(p)==0) return false; else return true; }
這里用到了fgets:
也就是:從流中獲得字符最多是size個(gè)到串s中;讀取成功返回這個(gè)s;否則返回NULL。
五、重定向判斷:
這里我們封裝的是redirect函數(shù)來(lái)完成;簡(jiǎn)單說(shuō)就是讓它檢查我們輸入的cl中是否有> < >>等重定向標(biāo)識(shí)符;然后根據(jù)左右分別是命令,文件等給它分離開(kāi)了;并給對(duì)應(yīng)的文件重定向(dup2一下):
預(yù)處理:首先利用標(biāo)識(shí)來(lái)枚舉一下重定向狀態(tài):輸出,輸入,還是追加:
下面就說(shuō)一下細(xì)節(jié)處理:
這里值得關(guān)注的是:我們從數(shù)組末尾開(kāi)始找
標(biāo)識(shí)符的;這樣然后利用覆蓋0的操作來(lái)完成前方命令的截?cái)啵号袛囗樞颍?lt; > >>;其次就是它可能文件前面存在空格;故我們?cè)贅?gòu)建一個(gè)去除空格函數(shù)。
? void eliminatesp(char cl[],int &fg){ while(isspace(cl[fg])) fg++; } void redirect(char cl[]){ status=NOPUT_RE; int start=0; int end=strlen(cl)-1; while(start<end){ if(cl[end]=='<'){ cl[end++] = 0; eliminatesp(cl, end); status = INPUT_RE; filename = cl+end; break; } else if(cl[end]=='>'){ if(cl[end-1]=='>'){ status=APPPUT_RE; cl[end-1]=0; } else{ status=OUTPUT_RE; cl[end]=0; } end++; eliminatesp(cl,end); filename = cl+end; break; } else{ end--; } } } ?
下面我們把獲得了重定向左邊的命令和右邊的文件下面就是利用dup2完成重定向操作了:
這里由于不是內(nèi)建命令;故我們還是放在子進(jìn)程來(lái)執(zhí)行:
if(status==INPUT_RE){ int fd=open(filename.c_str(),O_RDONLY); if(fd < 0) exit(1); dup2(fd,0); close(fd); } else if(status==OUTPUT_RE){ int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666); if(fd < 0) exit(1); dup2(fd,1); close(fd); } else if(status==APPPUT_RE){ int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND, 0666); if(fd < 0) exit(1); dup2(fd,1); close(fd); } else { }//NOPUT_RE無(wú)重定向操作
故我們只需當(dāng)分開(kāi)始fork后對(duì)我們標(biāo)志的進(jìn)行判斷即可;但是當(dāng)子進(jìn)程完成重定向執(zhí)行完退出后我們又要對(duì)status這個(gè)狀態(tài)給它重置一下。
六、語(yǔ)義分析:
簡(jiǎn)單來(lái)說(shuō)就是利用我們的strtok函數(shù)完成對(duì)空格的分割;然后把它填入到我們自己創(chuàng)建的argv數(shù)組中;注:這里最后也要補(bǔ)上NULL;注意好邊界處理:
bool cmdparse(char *c){ argc=0; std::string cc=c; // 淺拷貝: // if(_alias[cc]!="") c = &((_alias[cc])[0]); // 借助string的深拷貝賦值完成對(duì)hash內(nèi)數(shù)據(jù)的深拷貝: if(_alias[cc]!=""){ hash_cp =_alias[cc]; c=&(hash_cp[0]); } argv[argc++]=strtok(c,sp);//此處c這個(gè)指針將會(huì)隨之變化最后分割結(jié)束為null while(argv[argc++]=strtok(NULL,sp)){} argc--; return true; }
這里我們先暫時(shí)忽略對(duì)alias重命名的內(nèi)建命令的設(shè)計(jì)(后面會(huì)談到) 。
七、 內(nèi)建命令判斷:
下面我們把整體框架展示一下:
當(dāng)我們?cè)趍ain函數(shù)主體內(nèi)分析是不是內(nèi)建命令;如果是內(nèi)建命令那么就直接由main這個(gè)進(jìn)程執(zhí)行完然后直接開(kāi)始下一層循環(huán),就不往下走了;否則就走我們的execute函數(shù)。
//內(nèi)建命令:和execute執(zhí)行是分開(kāi)的 bool checkinkeycmd() { std::string cmd = argv[0]; if(cmd == "cd") { // printf("cd進(jìn)入!\n"); Cd(); return true; } else if(cmd == "echo") { Echo(); return true; } else if(cmd == "export") { // } else if(cmd == "alias"&&argc>=2) { // } return false; }
下面我們分四個(gè)部分來(lái)對(duì)相關(guān)內(nèi)建命令進(jìn)行單獨(dú)處理:
7.1 cd:
這里cd其實(shí)就是change directory;它完成的操作其實(shí)就是幫我們改變目錄;但是我們另外讓它把我們對(duì)應(yīng)的環(huán)境變量表也給改變;其實(shí)就要操作我們所維護(hù)的那個(gè)env數(shù)組了。
下面我們就不對(duì)應(yīng)把cd 的相關(guān)都實(shí)現(xiàn)一遍;大概實(shí)現(xiàn)常用的這幾個(gè):
注意:這里我們?yōu)榱丝梢詫?shí)現(xiàn)cd -:也就是會(huì)定義好變量保存上一次訪問(wèn)的目錄;方便回去;故當(dāng)每次chdir都會(huì)保存一下;并改變env表中的pwd
這里我們用的是string;也就是利用了它可以被const類型的字符串初始化;也可以通過(guò)c_str完成對(duì)應(yīng)轉(zhuǎn)換。
7.1.1 cd :
這里單純的cd也就是只有命令無(wú)參數(shù)此時(shí)argc=1;故直接跳到家目錄:
const char *gethome() { const char *home = getenv("HOME"); return home == NULL ? "" : home; }
保存原先目錄位置然后改變?cè)俑采wenv對(duì)應(yīng)pwd即可:
if(argc == 1) //直接返回到家目錄,但是此時(shí)沒(méi)有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標(biāo)記 借助string完成了否則對(duì)返回const多次覆蓋保存較為麻煩 { std::string home = gethome(); if(home.empty()) return false; std::string tmp=getpwd(); lastpwd=tmp; chdir(home.c_str()); getpwd(); return true; }
下面我們保存第二個(gè)參數(shù):
std::string where = argv[1];
效果展示:
7.1.2 cd -:
if(where=="-") //上一個(gè)工作目錄 { // std::cout<<lastpwd<<std::endl; chdir(lastpwd.c_str());//這里的lastpwd是我們?cè)谛虑袚Q目錄更改env前記錄的;故是先前的pwd getpwd(); }
效果展示:
7.1.3 cd / :
else if(where=="/"){ std::string tmp=getpwd(); lastpwd=tmp; chdir("/"); getpwd(); }
效果展示:
7.1.4 cd ~:
這里分為普通用戶還是root:普通用戶是家目錄而root就是登機(jī)目錄了:
if(!strcmp(getuser(),"root")){ std::string tmp=getpwd(); lastpwd=tmp; chdir("/root"); getpwd(); } else { std::string home = gethome(); std::string tmp=getpwd(); lastpwd=tmp; chdir(home.c_str()); getpwd(); }
效果展示:
7.1.5 cd +dirname:
else { std::string tmp=getpwd(); lastpwd=tmp; // std::cout<<lastpwd<<std::endl; chdir(where.c_str()); getpwd(); }
演示效果:
7.2 echo:
這里我們分為 echo $?;echo $+環(huán)境變量:
全局變量lastcode保存上次的子進(jìn)程退出碼;方便下一次打?。?/p>
對(duì)echo $?我們規(guī)定只要走了子進(jìn)程就會(huì)返回1;比如內(nèi)建命令等就返回0。
void Echo(){ //echo $? echo $PATH if(argc==2){ std::string func=argv[1]; if(func=="$?"){ std::cout << lastcode << std::endl; lastcode = 0; } else if(func[0]=='$'){ std::string envname = func.substr(1); const char * envvalue= getenv(envname.c_str()); if(envvalue) std::cout << envvalue << std::endl; } else { std::cout << func << std::endl; } } }
演示效果:
7.3 export:
這里我們只需在我們維護(hù)的env數(shù)組多開(kāi)一個(gè)空間然后把我們要導(dǎo)入的串記錄一下完成深拷貝(注意最后一個(gè)置空):
void Export(){ env_str=argv[1]; env[envs]=(char*)calloc(strlen(argv[1])+1,1); for(int i=0;i<env_str.size();i++)env[envs][i]=env_str[i]; envs++; env[envs]=NULL; }
效果展示:
當(dāng)我們退出后重新進(jìn)入:
發(fā)現(xiàn)沒(méi)了;符合我們的預(yù)期。
7.4 alias:
首先我們先認(rèn)識(shí)一下linux中的alias也就是起別名:
查看別名表:
起別名(alias 別名=原名):
使用效果:
刪除別名(unalias+別名):
這里比如我們的ll就是alias起別名來(lái)的;下面我們就來(lái)模擬實(shí)現(xiàn)一下(簡(jiǎn)單版本的alias)。
這里用到了映射,故我們采用了哈希表;
全局變量:
cur,pre是分別是別名和原名 ;
hash_cp是命令行分析過(guò)程的對(duì)hash表內(nèi)取得值的一個(gè)深拷貝;反之strtok函數(shù)破壞了;導(dǎo)致再次使用這個(gè)別名就會(huì)出現(xiàn)找原名時(shí)候被破壞的結(jié)果。
封裝Alias函數(shù):
void Alias(){ std::string sec=argv[1]; auto pos=sec.find('='); cur=sec.substr(0,pos); pre=sec.substr(pos+1,std::string::npos); for(int i=2;i<argc;i++){ pre+=" "; pre+=argv[i]; } _alias[cur]=pre; }
對(duì)語(yǔ)義分析部分修改:
argc=0; std::string cc=c; // 淺拷貝: // if(_alias[cc]!="") c = &((_alias[cc])[0]); // 借助string的深拷貝賦值完成對(duì)hash內(nèi)數(shù)據(jù)的深拷貝: if(_alias[cc]!=""){ hash_cp =_alias[cc]; c=&(hash_cp[0]); } argv[argc++]=strtok(c,sp);//此處c這個(gè)指針將會(huì)隨之變化最后分割結(jié)束為null while(argv[argc++]=strtok(NULL,sp)){} argc--; return true;
效果展示:
八、子進(jìn)程執(zhí)行操作:
main函數(shù)的進(jìn)程fork后讓子進(jìn)程得到相關(guān)指令和參數(shù)并用exec系列函數(shù)進(jìn)行程序替換(這里選用的execvp):
然后父進(jìn)程阻塞等待回收資源和相關(guān)信息:
int execute(){ pid_t pid=fork(); if(pid==0){ //printf("argv[0]: %s\n", argv[0]); if(status==INPUT_RE){ int fd=open(filename.c_str(),O_RDONLY); if(fd < 0) exit(1); dup2(fd,0); close(fd); } else if(status==OUTPUT_RE){ int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666); if(fd < 0) exit(1); dup2(fd,1); close(fd); } else if(status==APPPUT_RE){ int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND, 0666); if(fd < 0) exit(1); dup2(fd,1); close(fd); } else { }//NOPUT_RE無(wú)重定向操作 execvp(argv[0],argv); exit(1); } int status=0; pid_t id=waitpid(pid,&status,0); if(id > 0) { lastcode = WEXITSTATUS(status);//對(duì)于這里規(guī)定當(dāng)execute的子進(jìn)程執(zhí)行完就返回1;內(nèi)建命令或者其他都是返回0 } return 0; }
九、myshell代碼匯總:
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include<unordered_map> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> extern char**environ; #define CS 1024//命令行提示符最大值 #define FT "my simulate shell:%s@%s %s# "http://snprintf的format最大值 #define sp " "http://space #define MC 128//命令行參數(shù)最大值 //這里模擬了bash的兩張表,而不是main直接繼承下(改變environ指針)而是重新布置了一下數(shù)組,讓environ指針指向我們所布置的數(shù)組。 // 1. 命令行參數(shù)表 #define MAXARGC 128 char *argv[MAXARGC]; int argc = 0; // 2. 環(huán)境變量表 #define MAX_ENVS 100 char *env[MAX_ENVS]; int envs = 0; char cwd[1024]; char cwdenv[1029]; //char *lastpwd=(char*)calloc(1024,1); std::string lastpwd; int lastcode=0; //char export_env[1024]; std::string env_str; //對(duì)alias的適用: std::unordered_map<std::string,std::string>_alias; std::string cur,pre; std::string hash_cp; //重定向: std::string filename; #define NOPUT_RE 0 #define INPUT_RE 1 #define OUTPUT_RE 2 #define APPPUT_RE 3 int status;//重定向方式 void initenv(){ memset(env, 0, sizeof(env)); envs= 0; for(int i=0;environ[i];i++){ env[i]=(char*)malloc(strlen(environ[i])+1);//這里模擬的也可以不開(kāi)空間,直接棧上 strcpy(env[i], environ[i]); envs++; } env[envs] = NULL; // for(int i = 0; env[i]; i++) // { // putenv(env[i]); // } environ = env;//用自己生成的env表 } //獲取一些環(huán)境變量: const char* getuser(){ const char*u=getenv("USER"); return u==NULL?"NONE":u; } const char* gethostname(){ const char*h=getenv("HOSTNAME"); return h==NULL?"NONE":h; } //const char* getpwd(){ // const char*p=getenv("PWD"); // return p==NULL?"NONE":p; //} const char *gethome() { const char *home = getenv("HOME"); return home == NULL ? "" : home; } //這里獲得環(huán)境變量和其他上面不同;因?yàn)楫?dāng)我們通過(guò)chdir改變當(dāng)前目錄的時(shí)候它在環(huán)境變量中的記錄(真正的bash實(shí)現(xiàn)了)而我們沒(méi)有實(shí)現(xiàn),因此我們 //可以通過(guò)getcwd每次調(diào)完新的目錄開(kāi)始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時(shí)候變化 const char *getpwd(){ char *pwd = getcwd(cwd, sizeof(cwd)); if(pwd != NULL) { snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd); putenv(cwdenv);//put進(jìn)去的是環(huán)境變量的地址也就是一個(gè)指針指向的是那段量,因此指針要么是全局要么在堆上 } return pwd==NULL?"NONE":pwd; } //const char *getpwd(){ // // 調(diào)用了這個(gè),在Getpwd中 // return Getpwd(); //} //打印命令行提示符:把pwd的最后一個(gè)名稱得到 std::string convert(const char *pwd){ std::string s=pwd; auto pos=s.rfind('/'); if(pos==std::string::npos)return "error"; if(pos==s.size()-1)return "/"; else return s.substr(pos+1); } void ptcmdprompt(){ char p[CS]={0}; snprintf(p,sizeof(p),FT, getuser(),gethostname(),convert(getpwd()).c_str() ); printf("%s",p); //無(wú)\n及時(shí)刷新標(biāo)準(zhǔn)輸出緩沖區(qū) fflush(stdout); } //獲得命令行參數(shù): bool gtcmdline(char *p,int size){ char *j=fgets(p,size,stdin); // printf("參數(shù)是:%d",size); if(j==NULL)return false; //printf("%s\n",p); p[strlen(p)-1]=0;//干掉\n if(strlen(p)==0) return false; else return true; } //命令行解釋:把輸入的命令行參數(shù)分出來(lái)方便后序傳給要調(diào)用的main的argv bool cmdparse(char *c){ // printf("%s\n",c); argc=0; std::string cc=c; // std::cout<<"內(nèi)容:"<<_alias[cc]<<std::endl; // 淺拷貝: // if(_alias[cc]!="") c = &((_alias[cc])[0]); // 借助string的深拷貝賦值完成對(duì)hash內(nèi)數(shù)據(jù)的深拷貝: if(_alias[cc]!=""){ hash_cp =_alias[cc]; c=&(hash_cp[0]); } //printf("%s\n",c); argv[argc++]=strtok(c,sp);//此處c這個(gè)指針將會(huì)隨之變化最后分割結(jié)束為null while(argv[argc++]=strtok(NULL,sp)){} argc--; //printf("%s\n%s\n",argv[0],argv[1]); // printf("%s%s\n",argv[0],argv[1]); return true; } //void lastpwd(){ // // printf("11111111111111111"); // // printf(" %s%d\n ",argv[0],argc); // if(!strcmp(argv[0],"cd")&&argc==2){ // // printf("執(zhí)行\(zhòng)n"); // std::string s("LASTPWD"); // s+="="; // s+=argv[1]; // //std::cout<<s<<std::endl; // // char p[s.size()+1]={0}; // char* p = (char*)calloc(s.size() + 1, 1); // for(int i=0;i<s.size();i++) p[i]=s[i]; // printf("huanbian:%s\n",p); // environ[envs]=(char*)calloc(strlen(p)+1,1); // putenv(p); // } //} bool Cd(){ if(argc == 1) //直接返回到家目錄,但是此時(shí)沒(méi)有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標(biāo)記 借助string完成了否則對(duì)返回const多次覆蓋保存較為麻煩 { std::string home = gethome(); if(home.empty()) return false; std::string tmp=getpwd(); lastpwd=tmp; chdir(home.c_str()); getpwd(); return true; } else{ std::string where = argv[1]; // printf("%s\n",argv[1]); // cd - / cd ~ if(where=="-") //上一個(gè)工作目錄 { // std::cout<<lastpwd<<std::endl; chdir(lastpwd.c_str());//這里的lastpwd是我們?cè)谛虑袚Q目錄更改env前記錄的;故是先前的pwd getpwd(); } else if(where=="/"){ std::string tmp=getpwd(); lastpwd=tmp; chdir("/"); getpwd(); } else if(where=="~")//家目錄 { if(!strcmp(getuser(),"root")){ std::string tmp=getpwd(); lastpwd=tmp; chdir("~"); getpwd(); } else { std::string home = gethome(); std::string tmp=getpwd(); lastpwd=tmp; chdir(home.c_str()); getpwd(); } } // else if(where==".."){} 上級(jí)目錄 else { std::string tmp=getpwd(); lastpwd=tmp; // std::cout<<lastpwd<<std::endl; chdir(where.c_str()); getpwd(); } return true; } } void Echo(){ //echo $? echo $PATH if(argc==2){ std::string func=argv[1]; if(func=="$?"){ std::cout << lastcode << std::endl; lastcode = 0; } else if(func[0]=='$'){ std::string envname = func.substr(1); const char * envvalue= getenv(envname.c_str()); if(envvalue) std::cout << envvalue << std::endl; } else { std::cout << func << std::endl; } } } void Export(){ env_str=argv[1]; env[envs]=(char*)calloc(strlen(argv[1])+1,1); for(int i=0;i<env_str.size();i++)env[envs][i]=env_str[i]; envs++; env[envs]=NULL; } void Alias(){ std::string sec=argv[1]; auto pos=sec.find('='); cur=sec.substr(0,pos); pre=sec.substr(pos+1,std::string::npos); for(int i=2;i<argc;i++){ pre+=" "; pre+=argv[i]; } _alias[cur]=pre; } //分子進(jìn)程執(zhí)行: int execute(){ pid_t pid=fork(); if(pid==0){ //printf("argv[0]: %s\n", argv[0]); if(status==INPUT_RE){ int fd=open(filename.c_str(),O_RDONLY); if(fd < 0) exit(1); dup2(fd,0); close(fd); } else if(status==OUTPUT_RE){ int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_TRUNC, 0666); if(fd < 0) exit(1); dup2(fd,1); close(fd); } else if(status==APPPUT_RE){ int fd=open(filename.c_str(),O_CREAT | O_WRONLY | O_APPEND, 0666); if(fd < 0) exit(1); dup2(fd,1); close(fd); } else { }//NOPUT_RE無(wú)重定向操作 execvp(argv[0],argv); exit(1); } int status=0; pid_t id=waitpid(pid,&status,0); if(id > 0) { lastcode = WEXITSTATUS(status);//對(duì)于這里規(guī)定當(dāng)execute的子進(jìn)程執(zhí)行完就返回1;內(nèi)建命令或者其他都是返回0 } return 0; } //內(nèi)建命令:和execute執(zhí)行是分開(kāi)的 bool checkinkeycmd() { std::string cmd = argv[0]; if(cmd == "cd") { // printf("cd進(jìn)入!\n"); Cd(); return true; } else if(cmd == "echo") { Echo(); return true; } else if(cmd == "export") { Export(); } else if(cmd == "alias"&&argc>=2) { Alias(); } return false; } void eliminatesp(char cl[],int &fg){ while(isspace(cl[fg])) fg++; } void redirect(char cl[]){ status=NOPUT_RE; int start=0; int end=strlen(cl)-1; while(start<end){ if(cl[end]=='<'){ cl[end++] = 0; eliminatesp(cl, end); status = INPUT_RE; filename = cl+end; break; } else if(cl[end]=='>'){ if(cl[end-1]=='>'){ status=APPPUT_RE; cl[end-1]=0; } else{ status=OUTPUT_RE; cl[end]=0; } end++; eliminatesp(cl,end); filename = cl+end; break; } else{ end--; } } } void destroy(){ for(int i=0;env[i];i++){ free(env[i]); } } int main() { //自己的環(huán)境變量和命令行參數(shù)表的初始化: initenv(); while(1) { //命令提示行打?。? ptcmdprompt(); char cl[CS]={0}; //把命令參數(shù)輸入到cl while(!gtcmdline(cl,sizeof(cl))){} redirect(cl); //把命令參數(shù)這個(gè)串拆解到argv里: cmdparse(cl); //判斷是否是內(nèi)建命令由bash自己完成(這里模擬的是main自己執(zhí)行) if(checkinkeycmd()) { // lastpwd(); continue; } execute(); } //銷毀表所開(kāi)辟的空間 destroy(); }
目前功能比較基本,會(huì)不斷補(bǔ)充;感謝支持??! !
以上就是Linux命令行解釋器的模擬實(shí)現(xiàn)過(guò)程的詳細(xì)內(nèi)容,更多關(guān)于Linux命令行解釋器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Linux中g(shù)it用https連接時(shí)不用每次輸入密碼的方法
這篇文章主要給大家介紹了關(guān)于Linux中g(shù)it使用https連接時(shí)不用每次輸入密碼的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06linux系統(tǒng)下MongoDB單節(jié)點(diǎn)安裝教程
這篇文章主要給大家介紹了在linux系統(tǒng)下mongo在單節(jié)點(diǎn)安裝的方法教程,文中將實(shí)現(xiàn)的方法一步步介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧。2017-10-10Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問(wèn)題
這篇文章主要介紹了Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09