Linux命令行解釋器的模擬實現(xiàn)過程
一、整體框架:
首先我們把這個myshell大致進行框架展示出:

我們首先創(chuàng)建數(shù)組cl保存要輸入的字符串;而只要讀取失敗就要一直讀取故我們在獲取,命令行輸入的時候利用了while循環(huán);其次就是如果是內(nèi)建命令;我們就要直接父進程執(zhí)行完;無需execute再讓子進程執(zhí)行了。
先說一下想法:這里可執(zhí)行程序,把它當成真正shell的bash;大部分命令都是通過調(diào)用子進程來程序替換完成;有些命令是內(nèi)建的,故需要自己完成;而首先這個程序會繼承原本bash的那張環(huán)境變量表;這里我們模擬實現(xiàn)一下真正的bash的那兩張表:也就是說我們用數(shù)組,通過拷貝原bash的表,改變environ指針來維護我們的數(shù)組(也就是我們自己的可執(zhí)行程序要調(diào)用的那張環(huán)境變量表) :這里補充一點:對于環(huán)境變量如果我們env命令:它是通過environ指針來進行查找打印的;局部打印就不一定了。 后面我們具體實現(xiàn)的時候會有所體現(xiàn),之后我們道來。
然后下面就是一步步對這些拆開的函數(shù)進行實現(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ù)組進行一些增加/覆蓋環(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)因為我們維護的這張環(huán)境變量表,未添加其他修改功能,這里需要我們手動修改;這樣pwd就不會變了(當換目錄的時候):因此我們手動維護一下:
下面是我們要添加的全局變量(因為導入environ維護的二維數(shù)組應該是地址;故給它整成全局):

//這里獲得環(huán)境變量和其他上面不同;因為當我們通過chdir改變當前目錄的時候它在環(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進去的是環(huán)境變量的地址也就是一個指針指向的是那段量,因此指針要么是全局要么在堆上
}
return pwd==NULL?"NONE":pwd;
}普及一下用到的getcwd:

參數(shù):放入的數(shù)組;最大字節(jié)數(shù);成功返回這段pwd失敗就是NULL。
這里我們再做一個小優(yōu)化;也就是把路徑變短一下就乘當下目錄:
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()為了方便;以及后面很多要注意作用域:因此考慮了全局設計。
再下面就是命令行打印了:
void ptcmdprompt(){
char p[CS]={0};
snprintf(p,sizeof(p),FT, getuser(),gethostname(),convert(getpwd()).c_str() );
printf("%s",p);
//無\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中是否有> < >>等重定向標識符;然后根據(jù)左右分別是命令,文件等給它分離開了;并給對應的文件重定向(dup2一下):
預處理:首先利用標識來枚舉一下重定向狀態(tài):輸出,輸入,還是追加:

下面就說一下細節(jié)處理:
這里值得關(guān)注的是:我們從數(shù)組末尾開始找
標識符的;這樣然后利用覆蓋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)建命令;故我們還是放在子進程來執(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無重定向操作故我們只需當分開始fork后對我們標志的進行判斷即可;但是當子進程完成重定向執(zhí)行完退出后我們又要對status這個狀態(tài)給它重置一下。
六、語義分析:
簡單來說就是利用我們的strtok函數(shù)完成對空格的分割;然后把它填入到我們自己創(chuàng)建的argv數(shù)組中;注:這里最后也要補上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)建命令的設計(后面會談到) 。
七、 內(nèi)建命令判斷:
下面我們把整體框架展示一下:
當我們在main函數(shù)主體內(nèi)分析是不是內(nèi)建命令;如果是內(nèi)建命令那么就直接由main這個進程執(zhí)行完然后直接開始下一層循環(huán),就不往下走了;否則就走我們的execute函數(shù)。
//內(nèi)建命令:和execute執(zhí)行是分開的
bool checkinkeycmd()
{
std::string cmd = argv[0];
if(cmd == "cd")
{
// printf("cd進入!\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)建命令進行單獨處理:
7.1 cd:
這里cd其實就是change directory;它完成的操作其實就是幫我們改變目錄;但是我們另外讓它把我們對應的環(huán)境變量表也給改變;其實就要操作我們所維護的那個env數(shù)組了。
下面我們就不對應把cd 的相關(guān)都實現(xiàn)一遍;大概實現(xiàn)常用的這幾個:
注意:這里我們?yōu)榱丝梢詫崿F(xiàn)cd -:也就是會定義好變量保存上一次訪問的目錄;方便回去;故當每次chdir都會保存一下;并改變env表中的pwd

這里我們用的是string;也就是利用了它可以被const類型的字符串初始化;也可以通過c_str完成對應轉(zhuǎn)換。
7.1.1 cd :
這里單純的cd也就是只有命令無參數(shù)此時argc=1;故直接跳到家目錄:
const char *gethome()
{
const char *home = getenv("HOME");
return home == NULL ? "" : home;
}保存原先目錄位置然后改變再覆蓋env對應pwd即可:
if(argc == 1)
//直接返回到家目錄,但是此時沒有更改env的pwd,故我們后面調(diào)用getpwd()完成更改env標記 借助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就是登機目錄了:
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保存上次的子進程退出碼;方便下一次打?。?/p>

對echo $?我們規(guī)定只要走了子進程就會返回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:
這里我們只需在我們維護的env數(shù)組多開一個空間然后把我們要導入的串記錄一下完成深拷貝(注意最后一個置空):

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;
}效果展示:


當我們退出后重新進入:

發(fā)現(xiàn)沒了;符合我們的預期。
7.4 alias:
首先我們先認識一下linux中的alias也就是起別名:
查看別名表:

起別名(alias 別名=原名):

使用效果:

刪除別名(unalias+別名):

這里比如我們的ll就是alias起別名來的;下面我們就來模擬實現(xiàn)一下(簡單版本的alias)。
這里用到了映射,故我們采用了哈希表;
全局變量:

cur,pre是分別是別名和原名 ;
hash_cp是命令行分析過程的對hash表內(nèi)取得值的一個深拷貝;反之strtok函數(shù)破壞了;導致再次使用這個別名就會出現(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;效果展示:

八、子進程執(zhí)行操作:
main函數(shù)的進程fork后讓子進程得到相關(guān)指令和參數(shù)并用exec系列函數(shù)進行程序替換(這里選用的execvp):
然后父進程阻塞等待回收資源和相關(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ī)定當execute的子進程執(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)境變量和其他上面不同;因為當我們通過chdir改變當前目錄的時候它在環(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進去的是環(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及時刷新標準輸出緩沖區(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標記 借助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;
}
//分子進程執(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ī)定當execute的子進程執(zhí)行完就返回1;內(nèi)建命令或者其他都是返回0
}
return 0;
}
//內(nèi)建命令:和execute執(zhí)行是分開的
bool checkinkeycmd()
{
std::string cmd = argv[0];
if(cmd == "cd")
{
// printf("cd進入!\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();
}目前功能比較基本,會不斷補充;感謝支持!! !
以上就是Linux命令行解釋器的模擬實現(xiàn)過程的詳細內(nèi)容,更多關(guān)于Linux命令行解釋器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Linux中g(shù)it用https連接時不用每次輸入密碼的方法
這篇文章主要給大家介紹了關(guān)于Linux中g(shù)it使用https連接時不用每次輸入密碼的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-06-06
linux系統(tǒng)下MongoDB單節(jié)點安裝教程
這篇文章主要給大家介紹了在linux系統(tǒng)下mongo在單節(jié)點安裝的方法教程,文中將實現(xiàn)的方法一步步介紹的非常詳細,對大家學習或者使用具有一定的參考學習價值,需要的朋友們下面隨著小編來一起看看吧。2017-10-10
Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問題
這篇文章主要介紹了Linux下關(guān)于mtrace工具排查內(nèi)存泄露的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09

