Linux命令行解釋器的模擬實現(xiàn)過程
一、整體框架:
首先我們把這個myshell大致進(jìn)行框架展示出:
我們首先創(chuàng)建數(shù)組cl保存要輸入的字符串;而只要讀取失敗就要一直讀取故我們在獲取,命令行輸入的時候利用了while循環(huán);其次就是如果是內(nèi)建命令;我們就要直接父進(jìn)程執(zhí)行完;無需execute再讓子進(jìn)程執(zhí)行了。
先說一下想法:這里可執(zhí)行程序,把它當(dāng)成真正shell的bash;大部分命令都是通過調(diào)用子進(jìn)程來程序替換完成;有些命令是內(nèi)建的,故需要自己完成;而首先這個程序會繼承原本bash的那張環(huán)境變量表;這里我們模擬實現(xiàn)一下真正的bash的那兩張表:也就是說我們用數(shù)組,通過拷貝原bash的表,改變environ指針來維護(hù)我們的數(shù)組(也就是我們自己的可執(zhí)行程序要調(diào)用的那張環(huán)境變量表) :這里補(bǔ)充一點:對于環(huán)境變量如果我們env命令:它是通過environ指針來進(jìn)行查找打印的;局部打印就不一定了。 后面我們具體實現(xiàn)的時候會有所體現(xiàn),之后我們道來。
然后下面就是一步步對這些拆開的函數(shù)進(jìn)行實現(xiàn)了。
二、初始化myshell的環(huán)境變量表和命令行參數(shù)表:
這里我們自己開了兩個數(shù)組來模擬這兩張表;也就是拷貝父bash的那兩種表拷貝過來(簡單模擬一下)這倆張表的內(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);//這里模擬的也可以不開空間,直接棧上 strcpy(env[i], environ[i]); envs++; } env[envs] = NULL; // for(int i = 0; env[i]; i++) // { // putenv(env[i]); // } environ = env;//用自己生成的env表 }
這里我們的命令行參數(shù)表暫時不需要填充,但是需要把環(huán)境變量表由bash那里拷貝過來;并改變了environ指針指向,也就是說等我們執(zhí)行env操作的時候它就會打印我們的這個env數(shù)組了;比如后序我們用putenv等命令的話,它就會通過environ指針對我們的這個數(shù)組進(jìn)行一些增加/覆蓋環(huán)境變量的操作了。
三、命令行提示行的打?。?/h2>
我們讓它格式輸出這樣的格式:
#define FT "my simulate shell:%s@%s %s# "http://snprintf的format最大值
首先我們對比一下真正的命令解釋器:
我們此刻需要替換掉%s的就是通過環(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; }
但是這樣我們會發(fā)現(xiàn)因為我們維護(hù)的這張環(huán)境變量表,未添加其他修改功能,這里需要我們手動修改;這樣pwd就不會變了(當(dāng)換目錄的時候):因此我們手動維護(hù)一下:
下面是我們要添加的全局變量(因為導(dǎo)入environ維護(hù)的二維數(shù)組應(yīng)該是地址;故給它整成全局):
//這里獲得環(huán)境變量和其他上面不同;因為當(dāng)我們通過chdir改變當(dāng)前目錄的時候它在環(huán)境變量中的記錄(真正的bash實現(xiàn)了)而我們沒有實現(xiàn),因此我們 //可以通過getcwd每次調(diào)完新的目錄開始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時候變化 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)境變量的地址也就是一個指針指向的是那段量,因此指針要么是全局要么在堆上 } return pwd==NULL?"NONE":pwd; }
普及一下用到的getcwd:
參數(shù):放入的數(shù)組;最大字節(jié)數(shù);成功返回這段pwd失敗就是NULL。
這里我們再做一個小優(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對象的c_str接口就好;
這里順便說一下;因為后面很多都用到這一點:就是經(jīng)常操作的時候把char串變成string然后調(diào)用它的c_str()為了方便;以及后面很多要注意作用域:因此考慮了全局設(shè)計。
再下面就是命令行打印了:
void ptcmdprompt(){ char p[CS]={0}; snprintf(p,sizeof(p),FT, getuser(),gethostname(),convert(getpwd()).c_str() ); printf("%s",p); //無\n及時刷新標(biāo)準(zhǔn)輸出緩沖區(qū) fflush(stdout); }
下面展示下效果:
四、獲取命令參數(shù):
這里邏輯比較簡單就是把我們輸入的字符讀入然后把后面的\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個到串s中;讀取成功返回這個s;否則返回NULL。
五、重定向判斷:
這里我們封裝的是redirect函數(shù)來完成;簡單說就是讓它檢查我們輸入的cl中是否有> < >>等重定向標(biāo)識符;然后根據(jù)左右分別是命令,文件等給它分離開了;并給對應(yīng)的文件重定向(dup2一下):
預(yù)處理:首先利用標(biāo)識來枚舉一下重定向狀態(tài):輸出,輸入,還是追加:
下面就說一下細(xì)節(jié)處理:
這里值得關(guān)注的是:我們從數(shù)組末尾開始找
標(biāo)識符的;這樣然后利用覆蓋0的操作來完成前方命令的截斷:判斷順序:< > >>;其次就是它可能文件前面存在空格;故我們再構(gòu)建一個去除空格函數(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)程來執(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無重定向操作
故我們只需當(dāng)分開始fork后對我們標(biāo)志的進(jìn)行判斷即可;但是當(dāng)子進(jìn)程完成重定向執(zhí)行完退出后我們又要對status這個狀態(tài)給它重置一下。
六、語義分析:
簡單來說就是利用我們的strtok函數(shù)完成對空格的分割;然后把它填入到我們自己創(chuàng)建的argv數(shù)組中;注:這里最后也要補(bǔ)上NULL;注意好邊界處理:
bool cmdparse(char *c){ argc=0; std::string cc=c; // 淺拷貝: // if(_alias[cc]!="") c = &((_alias[cc])[0]); // 借助string的深拷貝賦值完成對hash內(nèi)數(shù)據(jù)的深拷貝: if(_alias[cc]!=""){ hash_cp =_alias[cc]; c=&(hash_cp[0]); } argv[argc++]=strtok(c,sp);//此處c這個指針將會隨之變化最后分割結(jié)束為null while(argv[argc++]=strtok(NULL,sp)){} argc--; return true; }
這里我們先暫時忽略對alias重命名的內(nèi)建命令的設(shè)計(后面會談到) 。
七、 內(nèi)建命令判斷:
下面我們把整體框架展示一下:
當(dāng)我們在main函數(shù)主體內(nèi)分析是不是內(nèi)建命令;如果是內(nèi)建命令那么就直接由main這個進(jìn)程執(zhí)行完然后直接開始下一層循環(huán),就不往下走了;否則就走我們的execute函數(shù)。
//內(nèi)建命令:和execute執(zhí)行是分開的 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; }
下面我們分四個部分來對相關(guān)內(nèi)建命令進(jìn)行單獨處理:
7.1 cd:
這里cd其實就是change directory;它完成的操作其實就是幫我們改變目錄;但是我們另外讓它把我們對應(yīng)的環(huán)境變量表也給改變;其實就要操作我們所維護(hù)的那個env數(shù)組了。
下面我們就不對應(yīng)把cd 的相關(guān)都實現(xiàn)一遍;大概實現(xiàn)常用的這幾個:
注意:這里我們?yōu)榱丝梢詫崿F(xiàn)cd -:也就是會定義好變量保存上一次訪問的目錄;方便回去;故當(dāng)每次chdir都會保存一下;并改變env表中的pwd
這里我們用的是string;也就是利用了它可以被const類型的字符串初始化;也可以通過c_str完成對應(yīng)轉(zhuǎn)換。
7.1.1 cd :
這里單純的cd也就是只有命令無參數(shù)此時argc=1;故直接跳到家目錄:
const char *gethome() { const char *home = getenv("HOME"); return home == NULL ? "" : home; }
保存原先目錄位置然后改變再覆蓋env對應(yīng)pwd即可:
if(argc == 1) //直接返回到家目錄,但是此時沒有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標(biāo)記 借助string完成了否則對返回const多次覆蓋保存較為麻煩 { std::string home = gethome(); if(home.empty()) return false; std::string tmp=getpwd(); lastpwd=tmp; chdir(home.c_str()); getpwd(); return true; }
下面我們保存第二個參數(shù):
std::string where = argv[1];
效果展示:
7.1.2 cd -:
if(where=="-") //上一個工作目錄 { // std::cout<<lastpwd<<std::endl; chdir(lastpwd.c_str());//這里的lastpwd是我們在新切換目錄更改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>
對echo $?我們規(guī)定只要走了子進(jìn)程就會返回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ù)組多開一個空間然后把我們要導(dǎo)入的串記錄一下完成深拷貝(注意最后一個置空):
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)沒了;符合我們的預(yù)期。
7.4 alias:
首先我們先認(rèn)識一下linux中的alias也就是起別名:
查看別名表:
起別名(alias 別名=原名):
使用效果:
刪除別名(unalias+別名):
這里比如我們的ll就是alias起別名來的;下面我們就來模擬實現(xiàn)一下(簡單版本的alias)。
這里用到了映射,故我們采用了哈希表;
全局變量:
cur,pre是分別是別名和原名 ;
hash_cp是命令行分析過程的對hash表內(nèi)取得值的一個深拷貝;反之strtok函數(shù)破壞了;導(dǎo)致再次使用這個別名就會出現(xiàn)找原名時候被破壞的結(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; }
對語義分析部分修改:
argc=0; std::string cc=c; // 淺拷貝: // if(_alias[cc]!="") c = &((_alias[cc])[0]); // 借助string的深拷貝賦值完成對hash內(nèi)數(shù)據(jù)的深拷貝: if(_alias[cc]!=""){ hash_cp =_alias[cc]; c=&(hash_cp[0]); } argv[argc++]=strtok(c,sp);//此處c這個指針將會隨之變化最后分割結(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無重定向操作 execvp(argv[0],argv); exit(1); } int status=0; pid_t id=waitpid(pid,&status,0); if(id > 0) { lastcode = WEXITSTATUS(status);//對于這里規(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; //對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);//這里模擬的也可以不開空間,直接棧上 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)境變量和其他上面不同;因為當(dāng)我們通過chdir改變當(dāng)前目錄的時候它在環(huán)境變量中的記錄(真正的bash實現(xiàn)了)而我們沒有實現(xiàn),因此我們 //可以通過getcwd每次調(diào)完新的目錄開始就使用它不僅能改變了env的pwd也就是新的位置;還能打印命令行提示符的時候變化 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)境變量的地址也就是一個指針指向的是那段量,因此指針要么是全局要么在堆上 } return pwd==NULL?"NONE":pwd; } //const char *getpwd(){ // // 調(diào)用了這個,在Getpwd中 // return Getpwd(); //} //打印命令行提示符:把pwd的最后一個名稱得到 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); //無\n及時刷新標(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ù)分出來方便后序傳給要調(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的深拷貝賦值完成對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這個指針將會隨之變化最后分割結(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) //直接返回到家目錄,但是此時沒有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標(biāo)記 借助string完成了否則對返回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=="-") //上一個工作目錄 { // std::cout<<lastpwd<<std::endl; chdir(lastpwd.c_str());//這里的lastpwd是我們在新切換目錄更改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==".."){} 上級目錄 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無重定向操作 execvp(argv[0],argv); exit(1); } int status=0; pid_t id=waitpid(pid,&status,0); if(id > 0) { lastcode = WEXITSTATUS(status);//對于這里規(guī)定當(dāng)execute的子進(jìn)程執(zhí)行完就返回1;內(nèi)建命令或者其他都是返回0 } return 0; } //內(nèi)建命令:和execute執(zhí)行是分開的 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ù)這個串拆解到argv里: cmdparse(cl); //判斷是否是內(nèi)建命令由bash自己完成(這里模擬的是main自己執(zhí)行) if(checkinkeycmd()) { // lastpwd(); continue; } execute(); } //銷毀表所開辟的空間 destroy(); }
目前功能比較基本,會不斷補(bǔ)充;感謝支持??! !
以上就是Linux命令行解釋器的模擬實現(xiàn)過程的詳細(xì)內(nèi)容,更多關(guān)于Linux命令行解釋器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Linux中g(shù)it用https連接時不用每次輸入密碼的方法
這篇文章主要給大家介紹了關(guān)于Linux中g(shù)it使用https連接時不用每次輸入密碼的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06linux系統(tǒng)下MongoDB單節(jié)點安裝教程
這篇文章主要給大家介紹了在linux系統(tǒng)下mongo在單節(jié)點安裝的方法教程,文中將實現(xiàn)的方法一步步介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起看看吧。2017-10-10Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問題
這篇文章主要介紹了Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09