【Linux】自己实现一个bash进程

bash就是命令行解释器,就是Linux操作系统让我们看到的,与用户进行交互的一种外壳(shell),当然了bash也是一个进程,它有时候就是通过创建子进程来执行我们输入的命令的。这无疑就离不开我们上篇博客所说的进程程序替换,就是让子进程去替换我们的命令进程,知道了它的原理,我们就可以试着自己写一个bash进程。

我们可以大体上对于整个过程来细分一下:

1.bash就是先打印命令行提示符,就是下面这个东西,获取用户输入的命令

命令行提示符由 [用户名@主机名 路径]# 这些构成

2.对于用户输入的字符串进行分割成命令

3.执行这个命令

其实大体上就是这三步,下面还有很多的细节问题,我们遇到再说

我们目前写的这个代码的结果是

为什么打印了两个换行?我们了解一下fgets这个函数

因为fgets会读入一个换行,就是我们敲完abc后的换行,它是不应该有的,我们要给它去掉

这样就可以把换行改成0了。

因为bash就是一只在等待用户输入指令,所以我们要把我们的程序写成一个循环。

还有一个问题,就是当我们什么都不输入,直接回车时,我们其实就没必要在执行下边了,直接回到循环最开始就可以,于是我们可以做一个判断就是如果输入的命令的长度为0,那么就直接回到循环最开始处,这里的长度可以让Interactive的返回值来给。

下一步就是对于字符串进行分割:

我们分割的话,函数可以将收到的字符串分割后放到一个全局变量中,方便后续的使用,我们C语言有一个分割字符串的函数,叫strtok

分割完了之后,我们就可以开始执行命令 了,就是让子进程执行,父进程等待。我们这里先是一些普通的需要子进程去执行的命令,因为有的命令需要父进程去执行,比如cd,需要父进程去切换路径,子进程是不行的

那么截止到现在,我们的程序已经可以像bash一样处理一些命令了,但是还不够,因为有一些内建命令,就是需要父进程去执行的:

这些命令,我们要在代码被子进程执行前先行判断,如果是内建命令,那么让父进程去执行,否则再让子进程去执行

我们这里先以cd命令 为例,如果判断出来了用户就是要输入cd命令,如果后边什么都没有,那么默认是回到家目录,如果有,那就chdir到那个目录,并且不要忘记命令行提示符可是一直通过环境变量PWD来打印我们当前所在目录的,所以我们还要通过putenv把PWD环境变量改一下,它默认是会覆盖的。

之后我们要知道export也是一个内建命令,export的作用是给自己设置一个环境变量,如果给子进程设置,那显然是不合理的,所以我们也需要处理一下。

下一个就是echo,我们的echo通常会有这么几种用法:

1.echo后什么都不加

2.后加$?表示打印最近一次进程的退出码

3.后随便打印一串字符

4.后加$环境变量,就打印环境变量的内容

下面是所有的代码,有不足的可以添加:

bash 复制代码
    1 #define _XOPEN_SOURCE                                                                                                                                  
    2 #include<stdio.h>
    3 #include<stdlib.h>
    4 #include<string.h>
    5 #include<unistd.h>
    6 #include<sys/types.h>
    7 #include<sys/wait.h>
    8 #include<stdlib.h>
    9 #define SIZE 1024
   10 #define MAX_ARGC 30//最大命令行字符串个数
   11 #define SEP " "//设置分隔符为空格
   12 char*argv[MAX_ARGC];
   13 int lastcode;//最近一次进程退出码
   14 int Interactive(char commandline[],int size)
   15 {
   16   printf("[%s@%s %s]$ ",getenv("USER"),getenv("HOSTNAME"),getenv("PWD"));
   17   fgets(commandline,size,stdin);
   18   commandline[strlen(commandline)-1]='\0';
   19   return strlen(commandline);
   20 }
   21 void Splitstr(char commandline[])
   22 {
   23   int i=0;
   24   argv[i++]=strtok(commandline,SEP);
   25   while(argv[i++]=strtok(NULL,SEP));
   26   if(strcmp("ls",argv[0])==0)
   27   {
   28     argv[i-1]="--color";
   29     argv[i]=NULL;
   30   }
   31 }
   32 void Execute()
   33 {                                                                                                                                                      
   34   if(strcmp("ll",argv[0])==0&&!argv[1])//特别处理ll
   35   {
   36     argv[0]="ls";
   37     argv[1]="-l";
   38   }
   39   pid_t id=fork();
   40   if(id==0)
   41   {
   42     execvp(argv[0],argv);
   43     printf("mybash: ");
   44     for(int i=0;argv[i];i++)printf("%s ",argv[i]);
   45     printf(": not found your command\n");
   46     exit(2);
   47   }
   48   int status=0;
   49   pid_t rid=waitpid(id,&status,0);
   50   if(rid>0)lastcode=WEXITSTATUS(status);
   51 }
   52 int Bulidincmd()
   53 {
   54   int ret=0;
   55   if(strcmp("cd",argv[0])==0)
   56   {
   57     ret=1;
   58     char*target=argv[1];
   59     if(!target)target=getenv("HOME");
   60     chdir(target);
   61     char tmp[SIZE];
   62     snprintf(tmp,SIZE,"PWD=%s",target);
   63     putenv(tmp);                                                                                                                                       
   64   }
   65   else if(strcmp("export",argv[0])==0)
   66   {
   67     ret=1;
   68     if(argv[1])
   69     {
   70       char tmp[SIZE];
   71       strncpy(tmp,argv[1]+1,strlen(argv[1])-2);
   72       putenv(tmp);
   73     }
   74   }
   75   else if(strcmp("echo",argv[0])==0)
   76   {
   77     ret=1;
   78     if(argv[1]&&argv[1][0]=='$')
   79     {
   80       if(argv[1][1]=='?'&&!argv[2])
   81       {
   82         printf("%d\n",lastcode);
   83         lastcode=0;
   84       }
   85       else
   86       {
   87         char* tmp=getenv(argv[1]+1);                                                                                                                   
   88         if(tmp)printf("%s\n",tmp);
   89       }
   90     }
   91     else
   92     {
   93         for(int i=1;argv[i];i++)printf("%s ",argv[i]);
   94         printf("\n");
   95     }
   96   }
   97   lastcode=0;
   98   return ret;
   99 }
  100 int main()
  101 {
  102   while(1)
  103   {
  104   char commandline[SIZE];
  105   //打印命令行提示符,获取用户输入的命令字符串
  106   int n=Interactive(commandline,SIZE);
  107   if(n==0)continue;
  108   //分割字符串成命令行参数
  109   Splitstr(commandline);
  110   //处理内建命令
  111   n=Bulidincmd();
  112   if(n==1)continue;
  113   //执行命令
  114   Execute(); 
  115  }
  116   return 0;
  117 }
相关推荐
梅见十柒17 分钟前
wsl2中kali linux下的docker使用教程(教程总结)
linux·经验分享·docker·云原生
Koi慢热20 分钟前
路由基础(全)
linux·网络·网络协议·安全
传而习乎30 分钟前
Linux:CentOS 7 解压 7zip 压缩的文件
linux·运维·centos
soulteary32 分钟前
突破内存限制:Mac Mini M2 服务器化实践指南
运维·服务器·redis·macos·arm·pika
我们的五年40 分钟前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
IT果果日记1 小时前
ubuntu 安装 conda
linux·ubuntu·conda
Python私教1 小时前
ubuntu搭建k8s环境详细教程
linux·ubuntu·kubernetes
羑悻的小杀马特1 小时前
环境变量简介
linux
小陈phd2 小时前
Vscode LinuxC++环境配置
linux·c++·vscode
运维&陈同学2 小时前
【zookeeper01】消息队列与微服务之zookeeper工作原理
运维·分布式·微服务·zookeeper·云原生·架构·消息队列