linux下编写一个仿shell

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 %[email protected]%s:%s ]",p1->pw_name,hostname,pathname);
  }
  else
  {
    printf("[DIY %[email protected]:%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 %[email protected]%s:%s ]",p1->pw_name,hostname,pathname);
  }
  else
  {
    printf("[DIY %[email protected]:%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;
}

结果:

时间: 2024-10-29 19:09:48

linux下编写一个仿shell的相关文章

在Linux下编写Daemon

在Linux(以Redhat Linux Enterprise Edition 5.3为例)下,有时需要编写Service.Service也是程序,一般随系统启动用户不干预就不退出的程序,可以称为Service.Linux下的Service一般称为Daemon. 以上是广义的Service的定义.Linux下的Service一般放在/etc/init.d文件夹下.浏览一下这个文件夹下的文件,可以发现在Linux下编写Service一般遵循的原则. 一 Linux下编写Service一般遵循的原则

Linux下编写驱动程序(VFS)

转:http://hi.baidu.com/firstm25/item/8fe022155e1fa78988a9568f 摘要:设备驱动程序是操作系统内核与机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节.那么驱动程序如何书写实现这一接口功能是本文讨论的重点,并以一简单的驱动程序介绍书写细节. 在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.(应用程序一般是在用户态下进行)也就是说系统必须在驱动程序的子函数返回后才能进行其它的工作,即驱动程序不能进入死循环. 字符型设备

linux下实现自己的shell解释器

实现一个自己的shell解释器,其原理比较简单,首先获取用户的输入,通过fork()函数获取两个进程(父子进程),子进程通过execvp()函数继续进行,此时父进程一直在等待子进程的结束,待都结束了就执行了一次shell解释. 1 /*============================================ 2 > Copyright (C) 2014 All rights reserved. 3 > FileName:my_shell.c 4 > author:dona

使用VI编辑器在Linux下编写Java文件

1.cd 文件名,进入一个目录下 2.vi 文件名,新建一个文件(如此文件已存在则打开) 进入编辑器 3.按i(光标所在输入)/按a(光标后输入)进入编辑模式,写入JAVA代码 P.S.  Esc退出编辑状态,非编辑状态下X为删除,HJKL分别为左上下右 4.按Esc退出编辑模式 5.按:wq,回车 6.ls查看文件,已存在 7.javac编译(文件名需加后缀) 8.java运行(文件名不加后缀) 使用VI编辑器在Linux下编写Java文件

如何在Linux下拷贝一个目录呢

cp -af newadmin/movie/.   uploadfile/mallvideo/ 如何在Linux下拷贝一个目录呢?这好像是再简单不过的问题了. 比如要把/home/usera拷贝到/mnt/temp,首先想到的就是 cp -R /home/usera/* /mnt/temp 但是这样有一个问题,/home/usera下的隐藏文件都不会被拷贝,子目录下的隐藏文件倒是会的. 那如何才是正确的方法呢?有人说用-a选项,有人说用find加管道. 其实没这么复杂,Google了之后,学了一

在ubuntu linux 中编写一个自己的python脚本

在ubuntu linux 中编写一个自己的简单的bash脚本. 实现功能:终端中输入简单的命令(以pmpy为例(play music python),为了区别之前说的bash脚本添加了py后缀),来实现音乐的播放.注:本人ununut中安装了audacious,所以就以audacious为例,来实现音乐的播放. 第一步:进入一个目录,最好是自己特别选定的,如果用文件浏览器可以新建一个名为pmpy空白文档文件:如果是用终端可以输入命令:vi pmpy(可能会因为位置问题,需要添加sudo) 第二

【转载】在Linux下,一个文件也有三种时间,分别是:访问时间、修改时间、状态改动时间

在windows下,一个文件有:创建时间.修改时间.访问时间.而在Linux下,一个文件也有三种时间,分别是:访问时间.修改时间.状态改动时间. 两者有此不同,在Linux下没有创建时间的概念,也就是不能知道文件的建立时间,但如果文件建立后就没有修改过,修改时间=建立时间;如果文件建立后,状态就没有改动过,那么状态改动时间=建立时间;如果文件建立后,没有被读取过,那么访问时间=建立时间,因为不好判断文件是否被改过.读过.其状态是否变过,所以判断文件的建立时间基本上能为不可能. 如何查一个文件的三

linux 下查看一个进程运行路径

在linux下查看进程大家都会想到用 ps -ef|grep XXX 可是看到的不是全路径,怎么看全路径呢? 每个进程启动之后在 /proc下面有一个于pid对应的路径 例如:ps -ef|grep python 显示:oracle    4431  4366  0 18:56 pts/2    00:00:00 python Server.py 4431就是进程号 到/proc/4431下,ls -l 会看到(需要root权限): 总用量 0 -r--r--r--    1 oracle  

Linux下模拟一个简易的消息机制

声明 #define MSG_ERROR  -1 #define MSG_SUCCEED 0 #define MSG_TRUE 1 #define MSG_FALSE 0 #define PM_NOREMOVE    0x00 #define PM_REMOVE    0x01 typedef unsigned long  WPARAM; typedef unsigned long  LPARAM; typedef unsigned int UINT; typedef int MSG_BOOL;