模拟实现Bash
1.Bash基本认识
2.Bash实现
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
char* commend_list[10]={NULL};
int main()
{
char commend[1024];//将命令存入这个数组
while(1)
{
cout<<"[lnb@VM-16-17centos 当前目录]";
fgets(commend,1024,stdin);
//去掉\n
commend[strlen(commend)-1]='\0';
//拆分命令
commend_list[0]=strtok(commend," ");
int i=1;
while(commend_list[i++]=strtok(NULL," "))
{}
// i=0;
// while(commend_list[i])
// {
// cout<<commend_list[i++]<<endl;
// }
//创建子进程
pid_t ret=fork();
if(ret>0)
{
//父进程,进行等待
int status=0;
waitpid(ret,&status,0);
cout<<"退出信号:"<<(status&0x7f)<<",退出码:"<<((status>>8)&0xff)<<endl;
cout<<"退出信号:"<<WTERMSIG(status)<<",退出码:"<<WEXITSTATUS(status)<<endl;
}
else
{
//子进程,进行程序替换
execvp(commend_list[0],commend_list);
exit(-1);
}
}
return 0;
}
3.添加细节
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
char* commend_list[10]={NULL};
int main()
{
char commend[1024];//将命令存入这个数组
while(1)
{
cout<<"[lnb@VM-16-17centos 当前目录]";
fgets(commend,1024,stdin);
//去掉\n
commend[strlen(commend)-1]='\0';
//拆分命令
commend_list[0]=strtok(commend," ");
int i=1;
while(commend_list[i++]=strtok(NULL," "))
{}
// i=0;
// while(commend_list[i])
// {
// cout<<commend_list[i++]<<endl;
// }
if(strcmp(commend_list[0],"ls")==0)
{
commend_list[i++]="--color=auto";
}
if(strcmp(commend_list[0],"ll")==0)
{
commend_list[0]="ls";
commend_list[1]="-l";
commend_list[2]="--color=auto";
commend_list[3]=NULL;
}
//创建子进程
pid_t ret=fork();
if(ret>0)
{
//父进程,进行等待
int status=0;
waitpid(ret,&status,0);
cout<<"退出信号:"<<(status&0x7f)<<",退出码:"<<((status>>8)&0xff)<<endl;
cout<<"退出信号:"<<WTERMSIG(status)<<",退出码:"<<WEXITSTATUS(status)<<endl;
}
else
{
//子进程,进行程序替换
execvp(commend_list[0],commend_list);
exit(-1);
}
}
return 0;
}
4.内置命令
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
char* commend_list[10]={NULL};
int main()
{
char commend[1024];//将命令存入这个数组
while(1)
{
cout<<"[lnb@VM-16-17centos 当前目录]";
fgets(commend,1024,stdin);
//去掉\n
commend[strlen(commend)-1]='\0';
//拆分命令
commend_list[0]=strtok(commend," ");
int i=1;
while(commend_list[i++]=strtok(NULL," "))
{}
// i=0;
// while(commend_list[i])
// {
// cout<<commend_list[i++]<<endl;
// }
if(strcmp(commend_list[0],"ls")==0)
{
commend_list[i++]="--color=auto";
}
if(strcmp(commend_list[0],"ll")==0)
{
commend_list[0]="ls";
commend_list[1]="-l";
commend_list[2]="--color=auto";
commend_list[3]=NULL;
}
if(strcmp(commend_list[0],"cd")==0)
{
if(commend_list[1]!=NULL)
chdir(commend_list[1]);
continue;
}
//创建子进程
pid_t ret=fork();
if(ret>0)
{
//父进程,进行等待
int status=0;
waitpid(ret,&status,0);
cout<<"退出信号:"<<(status&0x7f)<<",退出码:"<<((status>>8)&0xff)<<endl;
cout<<"退出信号:"<<WTERMSIG(status)<<",退出码:"<<WEXITSTATUS(status)<<endl;
}
else
{
//子进程,进行程序替换
execvp(commend_list[0],commend_list);
exit(-1);
}
}
return 0;
}
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
putenv("val=100");
while(1);//使用循环来保证环境变量不会随着程序结束而结束
return 0;
}
//test.cc
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>
extern char** environ;
int main(int argc,char* argv[],char *envp[])
{
//std::cout<<(void*)envp<<std::endl;
//std::cout<<(void*)environ<<std::endl;
putenv("val=111111111111111111111111111111111111111111111111111");
for(int i=0;environ[i];i++)
{
std:: cout<<environ[i]<<std::endl;
}
std::cout<<std::endl<<std::endl<<std::endl<<std::endl;
int ret=fork();
if(ret==0)
{
execl("/home/lnb/linux-l/24_review/24_11_13/replace","./replace");//程序替换成我们自己写的程序来查看环境变量
}
waitpid(ret,NULL,0);//父进程负责等待子进程
while(1)
{
}
return 0;
}
//relpace.cc
#include <iostream>
using namespace std;
int main(int argc,char* argv[],char* envp[])
{
for(int i=0;envp[i];i++)
{
cout<<envp[i]<<endl;
}
return 0;
}
//test.cc
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>
extern char** environ;
int main(int argc,char* argv[],char *envp[])
{
std::cout<<(void*)envp<<std::endl;//打印地址
std::cout<<(void*)environ<<std::endl;//打印地址
putenv("val=111111111111111111111111111111111111111111111111111");
for(int i=0;envp[i];i++)
{
std:: cout<<envp[i]<<std::endl;
}
std::cout<<std::endl<<std::endl<<std::endl<<std::endl;//和子进程打印的结果进行分割
int ret=fork();
if(ret==0)
{
execl("/home/lnb/linux-l/24_review/24_11_13/replace","./replace");//程序替换成我们自己写的程序来查看环境变量
}
waitpid(ret,NULL,0);//父进程负责等待子进程
while(1)
{
}
return 0;
}
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
extern char** environ;
char* commend_list[10]={NULL};
char environment[20][30]={0};//用来存储用户自己的环境变量
int envir_index=0;
int main()
{
char commend[1024];//将命令存入这个数组
while(1)
{
cout<<"[lnb@VM-16-17centos 当前目录]";
fgets(commend,1024,stdin);
//去掉\n
commend[strlen(commend)-1]='\0';
//拆分命令
commend_list[0]=strtok(commend," ");
int i=1;
while(commend_list[i++]=strtok(NULL," "))
{}
// i=0;
// while(commend_list[i])
// {
// cout<<commend_list[i++]<<endl;
// }
if(strcmp(commend_list[0],"ls")==0)
{
commend_list[i++]="--color=auto";
}
if(strcmp(commend_list[0],"ll")==0)
{
commend_list[0]="ls";
commend_list[1]="-l";
commend_list[2]="--color=auto";
commend_list[3]=NULL;
}
if(strcmp(commend_list[0],"cd")==0)
{
if(commend_list[1]!=NULL)
chdir(commend_list[1]);
continue;
}
if(strcmp(commend_list[0],"export")==0)
{
//我们要对这个它添加的环境变量进行一个保存,
//因为如果不保存会出现环境变量指针消失的问题
//commend_list[1]中存储了我们的添加的环境变量
//但是当我们下一回合重新读取命令的时候,他就会
//被覆盖,因为我们的commend_list数组中的指针都是
//从commend中出来的,这样的话就会导致结果有问题,
//无法正常显示
if(commend_list[1]!=NULL)
{
strcpy(environment[envir_index],commend_list[1]);
putenv(environment[envir_index++]);
continue;
}
}
if(strcmp(commend_list[0],"env")==0)
{
//之所以要进行特殊处理env是因为我们要打印显示的是bash父进程本身的环境变量
//而不是我们的子进程
for(int i=0;environ[i];i++)
{
cout<<environ[i]<<endl;
}
continue;
}
//创建子进程
pid_t ret=fork();
if(ret>0)
{
//父进程,进行等待
int status=0;
waitpid(ret,&status,0);
cout<<"退出信号:"<<(status&0x7f)<<",退出码:"<<((status>>8)&0xff)<<endl;
cout<<"退出信号:"<<WTERMSIG(status)<<",退出码:"<<WEXITSTATUS(status)<<endl;
}
else
{
//子进程,进行程序替换
execvp(commend_list[0],commend_list);
exit(-1);
}
}
return 0;
}
5.完整代码
在下面的完整代码中,我也加入了echo等其他部分的命令的处理,也进行了注释,相信大家也能看得懂
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
extern char** environ;
static int ret_code=0;//用来记录上一个进程的退出码
char* commend_list[10]={NULL};
char environment[20][30]={0};//用来存储用户自己的环境变量
int envir_index=0;
int main()
{
char commend[1024];//将命令存入这个数组
while(1)
{
cout<<"[lnb@VM-16-17centos 当前目录]";
fgets(commend,1024,stdin);
//去掉\n
commend[strlen(commend)-1]='\0';
//拆分命令
commend_list[0]=strtok(commend," ");
int i=1;
while(commend_list[i++]=strtok(NULL," "))
{}
// i=0;
// while(commend_list[i])
// {
// cout<<commend_list[i++]<<endl;
// }
if(strcmp(commend_list[0],"ls")==0)
{
commend_list[i++]="--color=auto";
}
if(strcmp(commend_list[0],"ll")==0)
{
commend_list[0]="ls";
commend_list[1]="-l";
commend_list[2]="--color=auto";
commend_list[3]=NULL;
}
if(strcmp(commend_list[0],"cd")==0)
{
if(commend_list[1]!=NULL)
chdir(commend_list[1]);
continue;
}
if(strcmp(commend_list[0],"export")==0)
{
//我们要对这个它添加的环境变量进行一个保存,
//因为如果不保存会出现环境变量指针消失的问题
//commend_list[1]中存储了我们的添加的环境变量
//但是当我们下一回合重新读取命令的时候,他就会
//被覆盖,因为我们的commend_list数组中的指针都是
//从commend中出来的,这样的话就会导致结果有问题,
//无法正常显示
if(commend_list[1]!=NULL)
{
strcpy(environment[envir_index],commend_list[1]);
putenv(environment[envir_index++]);
continue;
}
}
if(strcmp(commend_list[0],"env")==0)
{
//之所以要进行特殊处理env是因为我们要打印显示的是bash父进程本身的环境变量
//而不是我们的子进程
for(int i=0;environ[i];i++)
{
cout<<environ[i]<<endl;
}
continue;
}
if(strcmp(commend_list[0],"echo")==0&&(*(commend_list[1]))=='$')//对于查看环境变量值的处理
{
if(commend_list[1][1]=='?')//第一个命令选项中第二个字符
{
cout<<ret_code<<endl;
continue;
}
const char* str=NULL;
str=getenv(commend_list[1]+1);//此处的commend_list[1]的值为char*,指向第一个命令选项,+1后指向$后面
printf("%s:%s\n",commend_list[1]+1,str);
continue;
}
//创建子进程
pid_t ret=fork();
if(ret>0)
{
//父进程,进行等待
int status=0;
waitpid(ret,&status,0);
cout<<"退出信号:"<<(status&0x7f)<<",退出码:"<<((status>>8)&0xff)<<endl;
cout<<"退出信号:"<<WTERMSIG(status)<<",退出码:"<<WEXITSTATUS(status)<<endl;
ret_code=((status>>8)&0xff);
}
else
{
//子进程,进行程序替换
execvp(commend_list[0],commend_list);
exit(-1);
}
}
return 0;
}