在Linux下使用C语言模拟实现mybash

在Linux下使用C语言模拟实现mybash

学习Linux也有一段时间了,但是一直也没有写过总结。
打算今天就开始写一个Linux的学习过程总结的专栏,希望可以和大家一起学习和进步。

今天就先写一个在在Linux下使用C语言模拟实现mybash。
也就是我们仿写Linux系统中的bash(命令解释器)的操作。
(注意:我们只是模拟bash的执行命令的操作,没有bash本身的功能强大。)

了解bash

首先,我们先来了解bash,bash是命令解释器,而命令分为两种。

命令:
  • 内置命令:例如cd,exit,jobs等。
  • 普通命令:例如ps,ls,cp,kill等。

在我们用bash实现普通命令时,本质是用把bash父进程fork一个子进程,在exec(替换)一个命令(例如:ps/ls…),如图所示:

在这里插入图片描述
而在实现内置命令时,其实是发生在bash内部直接进行操作的,详见后文。

代码解读

printf_info()函数

printf_info()函数:
该函数是用于打印提示符,提示符格式如下:
用户名” + “@” + “主机名” + “:” + “当前位置” + “操作符” + “ ”。
为了更好符合Linux系统,我们还可以用printf()函数打印颜色。
下面分别介绍怎么获得用户名,主机名,当前位置,操作符。

用户名:
  • 可以通过getpwuid()函数获取一个结构体,结构体中包含用户名。

代码实现:

     struct passwd* p=getpwuid(id);//getpwuid()函数可以获取一个结构体,结构体中包含用户名
     if(p==NULL)
     {
         printf("$");
         fflush(stdout);//刷屏操作
         return ;
     }
主机名:
  • 可以通过gethostname()函数获取主机名。

代码实现:

     char hostname[128]={0};
     gethostname(hostname,128);
当前位置:
  • 可以通过getcwd()函数获取当前位置。

代码实现:

     char curr_dir[256]={0};
     getcwd(curr_dir,256);
操作符 :
  • 正常操作是默认为“$”
  • 管理员操作是默认为“#”

代码实现:

     char* s ="$";
     int id = getuid();
     if(id== 0)//Linux系统中管理员的uid为0
     {
         s="#";
     }
printf()函数打印颜色:

没想到吧,颜色其实可以用Linux打印出来,特别有意思。
大家可以去搜索一下printf()怎么打印颜色,这里就给大家展示一下,如图。
在这里插入图片描述
一个绿色的love送给大家,哈哈哈哈,是不是特别有意思呢。

为了和Linux系统本身有区别,我把用户名和主机名改为红色(默认为绿色),把当前位置改为黄色(默认为蓝色)。
在这里插入图片描述

代码实现:

printf("33[1;31m%s@%s33[0m:33[1;33m%s33[0m%s ",p->pw_name,hostname,curr_dir,s);
printf_info()函数总代码:
 void printf_info()
 {
     char* s ="$";
     int id = getuid();
     if(id== 0)
     {
         s="#";
     }
     struct passwd* p=getpwuid(id);
     if(p==NULL)
     {
         printf("$");
         fflush(stdout);
         return ;
     }
 
     char hostname[128]={0};
     gethostname(hostname,128);
 
     char curr_dir[256]={0};
     getcwd(curr_dir,256);
 
     printf("33[1;31m%s@%s33[0m:33[1;33m%s33[0m%s ",p->pw_name,hostname,curr_dir,s);
     fflush(stdout);
 }

get_cmd()函数

get_cmd()函数:
该函数用于获取命令。
利用strtok()函数可以把命令参数“切割”,这样我们的命令就可以带参数了。
下面就给大家介绍一下strtok()函数:

strtok()函数:
  • 该函数有两个参数,第一个参数是带分隔的字符串,第二个参数是分隔符。
  • 该函数返回值是分割的第一个字段
    在这里插入图片描述
  • 该函数会把每一个分隔符的地方转换为“”
  • 转化过程如下图示例所示:
    在这里插入图片描述
  • 详细的分割如下图所示(拿cp a.c b.c举例):
    在这里插入图片描述
    代码实现:
     int i=0;
     char* s =strtok(buff," ");
     while(s!=NULL)
     {
         myargv[i++]=s;
         s=strtok(NULL," ");
     }
get_cmd()函数总代码
 char* get_cmd(char buff[],char* myargv[])
 {
     if(buff==NULL||myargv==NULL)
     {
         return NULL;
     }
 
     int i=0;
     char* s =strtok(buff," ");
     while(s!=NULL)
     {
         myargv[i++]=s;
         s=strtok(NULL," ");
     }
     return myargv[0];
 }

内置命令解决方式

前文也说了,内置命令是在bash内部实现的
其实是通过输入的命令直接和内部比较,如果巧合是内置命令,实现操作就行了。

可能还不是很清楚,我们拿exit举例。

exit
  • 首先我们mybash内部的循环一定要设置为死循环(因为要不断输入命令),
  • 所以exit操作就特别简单,直接break退出循环就解决了。

代码实现:

   if(strcmp(cmd,"exit")==0)
   {
       break;
   }

如果还是不清楚,我们再来实现一下cd内置命令。

cd
  • cd第一个要参数不为空
  • 通过chdir()函数找到位置
  • 如果没有找到可以通过perror()输出错误原因

代码实现:

 if(strcmp(cmd,"cd")==0)
 {
    if(myargv[1]!=NULL)
    {
        if(chdir(myargv[1])!=0)
	    {
             perror("chdir err:");
        }
    }
 }

全代码如下:

 #include<stdio.h>
 #include<stdlib.h>
 #include<unistd.h>
 #include<string.h>
 #include<assert.h>
 #include<sys/wait.h>
 #include<pwd.h>
 
 #define ARGC    10
 
 char* get_cmd(char buff[],char* myargv[])
 {
     if(buff==NULL||myargv==NULL)
     {
         return NULL;
     }
 
     int i=0;
     char* s =strtok(buff," ");
     while(s!=NULL)
     {
         myargv[i++]=s;
         s=strtok(NULL," ");
     }
     return myargv[0];
 }
 
 void printf_info()
 {
     char* s ="$";
     int id = getuid();
     if(id== 0)
     {
         s="#";
     }
     struct passwd* p=getpwuid(id);
     if(p==NULL)
     {
         printf("$");
         fflush(stdout);
         return ;
     }
 
     char hostname[128]={0};
     gethostname(hostname,128);
 
     char curr_dir[256]={0};
     getcwd(curr_dir,256);
 
     printf("33[1;31m%s@%s33[0m:33[1;33m%s33[0m%s ",p->pw_name,hostname,curr_dir,s);
     fflush(stdout);
 }

 int main()
 {
     while(1)
     {
         printf_info();
 
         char buff[128]={0};
         fgets(buff,128,stdin);
 
         buff[strlen(buff)-1]=0;
 
         char* myargv[ARGC]={0};
         char* cmd = get_cmd(buff,myargv);
 
         if(cmd==NULL)//空格刷屏
         {
             continue;
         }
 
         if(strcmp(cmd,"exit")==0)
         {
             break;
         }
         else if(strcmp(cmd,"cd")==0)
         {
             if(myargv[1]!=NULL)
             {
                 if(chdir(myargv[1])!=0)
                 {
                     perror("chdir err:");
                 }
             } 
         }
   	    else
         {
             pid_t pid =fork();
             if(pid==-1)
             {
                 continue;
             }
             if(pid == 0)
             {
                 execvp(cmd,myargv);
                 printf("exec errn");
                 exit(0);
             }
             wait(NULL);
         }
     }
 }

下面我们上效果图:
在这里插入图片描述

最后

第一次写这么长的文章,很用心在写,以后也会陆续更新在Linux学习的过程。
如果有什么问题都可以评论或者私聊,我们一起讨论。
最后,如果觉得这篇文章对你有帮助的话,麻烦点点赞啦,谢谢。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>