Bootstrap

Linux 写一个自己的bash2.0版本

写一个自己的bash2.0版本


在上一次我们写了一个mybash程序,生成了一个自己的简单的bash。通过执行mybash可执行程序,调用命令,如ps,ls等。原理是mybash调用fork()产生一个子进程然后再替换该子进程,详细请见上一篇博客 写一个自己的bash1.0版本但是仍有一些缺陷,比如, 1、我们的命令提示符是写死的,是通过printf()函数直接打印出来的 printf(“wys@DESKTOP-2OU3HRV:~$”);那么现在来回顾一下Linux终端的命令提示符各部分的含义
在这里插入图片描述
明白了各部分含义后,在
*今天的2.0版本中首先得任务是改进命令提示符***,不要写死,让程序自己获取用户名,主机名,还有当前目录并识别当前是普通用户还是管理员用户。 2、像ps,ls,cp等等命令是在/usr/bin/目录下的,我们可以通过fork()一个子进程进而替换子进程来执行,但是还有一些命令是内置命令,比如cd ,jobs,exit等是在bin目录下找不到的,所以只能通过我们自己来实现, 今天2.0版本第二个任务就是自己简单实现cd命令
在这里插入图片描述

1、改进命令提示符

1.1获取用户名

参考Linux程序设计中文第四版 4.5用户信息
每一个用户都有一个唯一的用户标识符UID(管理员用户UID为0)。既然UID是用户身份的关键,那么我们就从UID开始吧!!!
UID有自己的类型–uid_t;
获取UID方法 uid_t getuid(void);
得到了用户UID后就可以通过UID来获取用户信息,方法:*struct passwd getpwuid(uid_t uid);
代码如下:

 30     int id = getuid();//获取用户id
 31     char* s = "$";
 32     if( id  == 0 )
 33     {
 34        s = "#";
 35     }
 36
 37     struct passwd* ptr = getpwuid(id);//通过uid获取用户详细信息
 38     if( ptr == NULL )
 39     {
 40       printf("$");
 41       fflush(stdout);
 42       return;
 43     }

1.2获取主机名

参考Linux程序设计中文第四版 4.6主机信息
通过gethostname函数获取
*int gethostname(char name,size_t namelean);
gethostname函数把机器的网络名写入name字符串。该字符串至少有namelen个字长,成功时,gethostbyname返回0,否则返回-1。
代码如下:

 45     char hostname[64] = { 0 };
 46     if (gethostname(hostname,64) == -1 )
 47     {
 48       printf("$");
 49       fflush(stdout);
 50       return;
 51     }

1.3获取当前位置

参考Linux程序设计中文第四版 3.7.5chdir系统调用和getcwd函数
程序可以通过调用getcwd函数来确定自己的当前工作目录
**#include <unistd.h>
char getcwd(char buff,size_t size);
getcwd函数把当前目录的名字写到给定的缓冲区buff里。如果目录名的长度超出了参数size的缓冲区长度(一个ERANGE错误),比如你所在的目录特别深,它就返回NULL,如果成功,它返回指针buf。
代码如下:

 54     char pwd_buff[128] = {0};
 55     if(getcwd(pwd_buff,128) == NULL)
 56     {
 57       printf("$");
 58       fflush(stdout);
 59       return;
 60     }

1.4整个void print_info()函数完整代码

 27 void print_info()
 28 {
 29
 30     int id = getuid();//获取用户id
 31     char* s = "$";
 32     if( id  == 0 )
 33     {
 34        s = "#";
 35     }
 36
 37     struct passwd* ptr = getpwuid(id);//通过uid获取用户详细信息
 38     if( ptr == NULL )
 39     {
 40       printf("$");
 41       fflush(stdout);
 42       return;
 43     }
 44
 45     char hostname[64] = { 0 };
 46     if (gethostname(hostname,64) == -1 )
 47     {
 48       printf("$");
 49       fflush(stdout);
 50       return;
 51     }
 52
 53     //获取当前位置
 54     char pwd_buff[128] = {0};
 55     if(getcwd(pwd_buff,128) == NULL)
 56     {
 57       printf("$");
 58       fflush(stdout);
 59       return;
 60     }
 61
 62     //打印命令提示行信息
 63     printf("\033[1;32;40m %s@%s:\033[0m \033[1;34;40m%s%s\033[0m",ptr->pw_name,hostname,pwd_buff,s);
 64     fflush(stdout);
 65 }

2、实现简单的cd命令操作

参考Linux程序设计中文第四版 3.7.5chdir系统调用和getcwd函数
程序可以像用户在文件系统里那样来浏览目录。就像你在shell里使用cd命令来切换目录一样,程序使用的是chdir系统调用。
#include<unistd.h>
int chdir(const char *path);

如果是cd命令,传入的第二个参数,即要切换的目录为空,则结束本次循环,再次打印出命令提示符(Linux bash中当cd后没有目录时,则切换到家目录,自己的mybash没有必要和bash相同,也可以自己设计),不为空时,使用chdir系统调用,操作成功则continue结束本次循环,重新打印当前路径。
代码如下:

 86     if (strcmp(cmd,"cd") == 0)
 87     {
 88         if( myargv[1] == NULL )
 89         {
 90             continue;
 91         }
 92         if ( chdir(myargv[1]) == -1)
 93         {
 94             perror("cd err");
 95         }
 96         continue;
 97     }

3、mybash完整代码

 1 #include<stdio.h>
  2 #include<string.h>
  3 #include<stdlib.h>
  4 #include<unistd.h>
  5 #include<sys/wait.h>
  6 #include<pwd.h>
  7 #include<errno.h>
  8
  9 char* get_cmd (char buff[],char* myargv[])
 10 {
 11
 12    if( buff == 0 || myargv == 0)
 13    {
 14       return NULL;
 15
 16    }
 17    char*s = strtok(buff," ");
 18    int i = 0;
 19    while( s != NULL )
 20    {
 21       myargv[i++] = s;
 22       s = strtok(NULL," ");
 23    }
 24    return myargv[0];
 25 }
 26
 27 void print_info()
 28 {
 29
 30     int id = getuid();//获取用户id
 31     char* s = "$";
 32     if( id  == 0 )
 33     {
 34        s = "#";
 35     }
 36
 37     struct passwd* ptr = getpwuid(id);//通过uid获取用户详细信息
 38     if( ptr == NULL )
 39     {
 40       printf("$");
 41       fflush(stdout);
 42       return;
 43     }
 44
 45     char hostname[64] = { 0 };
 46     if (gethostname(hostname,64) == -1 )
 47     {
 48       printf("$");
 49       fflush(stdout);
  50       return;
 51     }
 52
 53     //获取当前位置
 54     char pwd_buff[128] = {0};
 55     if(getcwd(pwd_buff,128) == NULL)
 56     {
 57       printf("$");
 58       fflush(stdout);
 59       return;
 60     }
 61
 62     //打印命令提示行信息
 63     printf("\033[1;32;40m %s@%s:\033[0m \033[1;34;40m%s%s\033[0m",ptr->pw_name,hostname,pwd_buff,s);
 64     fflush(stdout);
 65 }
     
 66 int main()
 67
 68 {
 69   while(1)
 70   {
 71     char buff[128] = { 0 };
 72     print_info();
 73     fgets(buff,128,stdin);
 74     buff[strlen(buff) - 1] = 0;
 75     char* myargv[10] = { 0 };
 76     char* cmd = get_cmd(buff,myargv);
 77     if(cmd == NULL)
 78     {
 79         continue;
 80     }
 81
 82     else if (strcmp(cmd,"exit") == 0 )
 83     {
 84        exit(0);
 85     }
 86     else if (strcmp(cmd,"cd") == 0)
 87     {
 88         if( myargv[1] == NULL )
 89         {
 90             continue;
 91         }
 92         if ( chdir(myargv[1]) == -1)
 93         {
 94             perror("cd err");
 95         }
 96         continue;
 97     }
 98     else
 99     {
100        pid_t pid = fork();
101        if(pid == -1 )
102        {
103          printf("fork error\n");
104        }
105
106        if(pid == 0 )
107        {
108          execvp(cmd,myargv);
109          printf("exec error\n");
110          exit(0);
111        }
112       wait(NULL);
113     }
114
115
116   }
117   exit(0);
118 }

4、编译运行程序

在这里插入图片描述
通过运行,我们发现mybash已经自动获取用户名,主机名,当前目录,以及是普通用户还是管理员用户,也实现了cd命令。就此mybash2.0版本结束了!!!

;