您的位置:首页 > 运维架构 > Shell

linux下编写一个仿shell

2016-06-25 22:21 344 查看
1、首先了解shell的基本框架



如上图所示
[用户名@主机名 当前路径]$ 命令
执行命令结果
目标:完成一个简单的shell(输入命令可以得到执行结果)
所以框架分为:
1、【提示符】$的显示 -----一堆函数的调用即可
2、 命令的执行-----读入数据,进行解析,得到argv[],执行execvp
3、 对于内置命令cd的单独处理
#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
void prompt()
{
struct passwd * p1;
char username[64];
char hostname[256];
char pathname[1024];
//获取用户名
p1=getpwuid(getuid());
//获取路径
getcwd(pathname,sizeof(pathname));
//获取主机名---返回0正确
if(gethostname(hostname,sizeof(hostname))==0)
{
printf("[DIY %s@%s:%s ]",p1->pw_name,hostname,pathname);
}
else
{
printf("[DIY %s@unkown:%s ]",p1->pw_name,pathname);
}
if(geteuid()==0)
{
printf("#");
}
else
{
printf("$");
}
}
int main()
{
while(1)
{
prompt();
fflush(stdout);
//获得环境变量
char buf[1024];
memset(buf,0,sizeof(buf));
size_t size=read(0,buf,sizeof(buf)-1);

if(size>0)
{
buf[size-1]='\0';
}
// printf("%s\n",buf);
char *p=buf;
int i=0;
char * my_argv[64]={0};
my_argv[0]=p;
while(*p!=0)
{
if(*p==' ')
{
*p='\0';
++p;
my_argv[++i]=p;
}
else
{
++p;
}
}
pid_t id=fork();

if(id==0) //子进程
{

if(strcmp(my_argv[0],"cd")==0)
{
chdir(my_argv[1]);
}
else
{
execvp(my_argv[0],my_argv);
}
}
else  //父进程
{
pid_t ret=waitpid(id,NULL,0);
}
}
return 0;
}



这样看好像一切都辣么完美~~~
然而却没办法退出,除非按control+z键强行退出
于是,我想是不是exit也可以实现
if(id==0)
{
if(strcmp(my_argv[0],"exit")==0)
{
exit(0);
}
...
}
else
{
pid_t ret=waitpid(id,NULL,0);
if(strcmp(my_argv[0],"exit")==0)
{
exit(0);
}
}
}
return 0;
}
结果是:



然而接下来这个exit做出的事情就比较神奇了



用了两次cd,pwd操作,然而exit却要执行三次才可以退出,这是为什么呢?
于是为了调试我将所有指令执行的进程的id及其父进程的id进行了打印分析





结论:
ls:第一次创建的子进程在执行完execvp之后终止
cd..第二次创建的子进程在执行chdir并不会终止
cd..第三次创建的子进程其父进程是第二次创建的子进程且不会终止
当执行exit,会从里到外一层一层的终止,然后才退出整个程序
改进方案:
将cd执行的函数写入到父进程
#include <stdio.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
void prompt()
{
struct passwd * p1;
char username[64];
char hostname[256];
char pathname[1024];
//获取用户名
p1=getpwuid(getuid());
//获取路径
getcwd(pathname,sizeof(pathname));
//获取主机名---返回0正确
if(gethostname(hostname,sizeof(hostname))==0)
{
printf("[DIY %s@%s:%s ]",p1->pw_name,hostname,pathname);
}
else
{
printf("[DIY %s@unkown:%s ]",p1->pw_name,pathname);
}
if(geteuid()==0)
{
printf("#");
}
else
{
printf("$");
}
}
int main()
{
while(1)
{
prompt();
fflush(stdout);
char buf[1024];
memset(buf,0,sizeof(buf));
size_t size=read(0,buf,sizeof(buf)-1);

if(size>0)
{
buf[size-1]='\0';
}
// printf("%s\n",buf);
char *p=buf;
int i=0;
char * my_argv[64]={0};
my_argv[0]=p;
while(*p!=0)
{
if(*p==' ')
{
*p='\0';
++p;
my_argv[++i]=p;
}
else
{
++p;
}
}
pid_t id=fork();
if(id==0)
{
if(strcmp(my_argv[0],"exit")==0)
{
exit(0);
}
if(strcmp(my_argv[0],"cd")==0)
{
exit(0);
}
else
{
execvp(my_argv[0],my_argv);
}
}
else
{
pid_t ret=waitpid(id,NULL,0);
if(strcmp(my_argv[0],"cd")==0)
{
chdir(my_argv[1]);
}
if(strcmp(my_argv[0],"exit")==0)
{
exit(0);
}
}
}
return 0;
}
结果:



本文出自 “momo就是辣么萌” 博客,请务必保留此出处http://momo462.blog.51cto.com/10138434/1792908
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: