【Linux】自主shell编写

如果学习了常见的Linux命令,exec*函数,环境变量,那你就可以尝试一下写一个简单的shell;

下面跟着我的步骤来吧!!🤩🤩

输入命令行

既然要写一个shell,我们第一步先把这个输入命令行打印出来:

观察一下:命令行中有三个环境变量,我们如何查找它们呢?

这就需要使用env查找:发现USER、HOSTNAME、PWD;

 怎么用代码来获取这三个环境变量对应的值呢?

用:getenv函数

有关函数更详细的内容可以区man手册上区查看

接下来,我们就可以写代码了:(主要代码,完整代码在文章最后)

  void Makecommandline()
  {
      char cmd[SIZE];
      const char *name=Getname();
      const char *hostname = Gethostname();
      const char *cwd =Getcwd();
      snprintf(cmd,sizeof(cmd),"[%s@%s %s]> ",name,hostname,cwd);
      printf("%s",cmd);
      fflush(stdout);
  }

获取用户命令

想一下,我们输入一个命令时,有时候是ls,有时候是ls -l -a ,输入的有空格怎么办呢?🤖

这时就可以用fgets函数来输入命令;

 void Getusercommand(char Usercmd[])
 {
     char *s = fgets(Usercmd,sizeof(Usercmd),stdin);
     if(s==NULL)
     {
         perror("fgets");
         exit(1);
     }
     Usercmd[strlen(Usercmd)-1]='\0';
 }

命令行字符串分割

获取到用户命令后,接下来我们要对这个命令进行分割:

分割用的是strtok函数;

代码:

void Splitcommand(char Usercmd[])
 {
     argv[0]=strtok(Usercmd,SEP);
     int index=1;
     while(argv[index++]=strtok(NULL,SEP));
 }

执行命令

完成上述的准备工作后,我们要执行我们输入的命令,那要怎么执行呢?

🤡当然是用的我们的exec*函数喽,这里根据实际情况,我们选择的应该是execvp()函数;

💥💥注意:我们使用exec*函数时,要创建一个子进程来进行,这样才不会使父进程中后续代码被覆盖!!

代码:

void Executecommand()
 {
     pid_t id =fork();
     if(id<0)
     {
         exit(1);
     }
     else if(id==0)
     {
         execvp(argv[0],argv);
         exit(errno);
     }
     else
     {
         int status=0;
         pid_t rid =waitpid(id,&status,0);
         if(rid>0)
         {
             //wait sucess
             lastcode = WEXITSTATUS(status);
             if(lastcode!=0)
             {
                 printf("%s:%s:%d\n",argv[0],strerror(lastcode),lastcode);
             }
         }
     }
 }

检测命令是否是内建命令

😺😺完成上述的四部后,你就已经完成了一个简单的shell,但是并不完整;哪里不完整呢?我们可以输入一个内建命令,比如cd命令,这时我们就找到了要完善的地方;

怎么完善呢?

我们要判断一下这个命令是不是内建命令,怎么判断呢,非常简单,直接if语句:

如果有其他的内建命令,直接添加即可;

Cd():如果是内建cd命令,看一下argv[1]是不是空,

(1)如果是空,我们输入的命令就是cd,那不就是直接回到家目录了嘛,如果不为空,就不要变了;

(2)接下来,直接改变cwd即可,用chdir函数;

(3)修改环境变量PWD的值,用putenv函数;

💥💥注意:putenv 用于将一个字符串添加到环境变量中,或者修改已经存在的环境变量。这个字符串的格式通常是 "NAME=VALUE",其中 NAME 是环境变量的名称,VALUE 是其对应的值。

你已经完成了一个基础的shell,更完善的shell还会继续更新!!

完整代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<sys/types.h>
  6 #include<sys/wait.h>
  7 #include<errno.h>
  8 
  9 # define SkipPath(p) do{p+=(strlen(p)-1);while(*p!='/')p--;}while(0)
 10 
 11 
 12 #define SIZE 512
 13 #define SEP " "
 14 
 15 
 16 char cwd[SIZE*2];
 17 int lastcode =0;
 18 char *argv[SIZE*2];
 19 
 20 const char *Getname()
 21 {
 22     const char *name=getenv("USER");
 23     if(name==NULL)return "None";
 24     return name;
 25 }
 26 
 27 
 28 const char *Gethostname()
 29 {
 30     const char *hostname=getenv("HOSTNAME");
 31     if(hostname==NULL)return "None";
 32     return hostname;
 33 }
 34 
 35 
 36 const char *Getcwd()
 37 {
 38     const char *pwd=getenv("PWD");
 39     if(pwd==NULL)return "None";
 40     return pwd;
 41 }
 42 
 43 void Makecommandline()
 44 {
 45     char cmd[SIZE];
 46     const char *name=Getname();
 47     const char *hostname = Gethostname();
 48     const char *cwd =Getcwd();
 49     SkipPath(cwd);
 50     snprintf(cmd,sizeof(cmd),"[%s@%s %s]> ",name,hostname,strlen(cwd)==1 ? "/":cwd+1);
 51     printf("%s",cmd);
 52     fflush(stdout);
 53 }
 54 
 55 void Getusercommand(char Usercmd[])
 56 {
 57     char *s = fgets(Usercmd,sizeof(Usercmd),stdin);
 58     if(s==NULL)
 59     {
 60         perror("fgets");
 61         exit(1);
 62     }
 63     Usercmd[strlen(Usercmd)-1]='\0';
 64 }
 65 
 66 void Splitcommand(char Usercmd[])
 67 {
 68     argv[0]=strtok(Usercmd,SEP);
 69     int index=1;
 70     while(argv[index++]=strtok(NULL,SEP));
 71 }
 72 const char *Gethome()
 73 {
 74     const char *home =getenv("HOME");
 75     if(home==NULL)return "/root";
 76     return home;
 77 }
 78 void Cd()
 79 {
 80     const char *path =argv[1];
 81     if(path==NULL)
 82     {
 83         //返回家目录
 84         path = Gethome();
 85     }
 86     chdir(path);
 87     char tmp[SIZE*2];
 88     getcwd(tmp,sizeof(tmp));
 89     snprintf(cwd,sizeof(cwd),"PWD=%s",tmp);
 90     putenv(cwd);
 91     //printf("%s\n",cwd);
 92 }
 93 int Checkbuildin()
 94 {
 95     int yes=0;
 96     if(strcmp(argv[0],"cd")==0)
 97     {
 98         yes=1;
 99         Cd();
100     }
101     return yes;
102 }
103 void Executecommand()
104 {
105     pid_t id =fork();
106     if(id<0)
107     {
108         exit(1);
109     }
110     else if(id==0)
111     {
112         execvp(argv[0],argv);
113         exit(errno);
114     }
115     else
116     {
117         int status=0;
118         pid_t rid =waitpid(id,&status,0);
119         if(rid>0)
120         {
121             //wait sucess
122             lastcode = WEXITSTATUS(status);
123             if(lastcode!=0)
124             {
125                 printf("%s:%s:%d\n",argv[0],strerror(lastcode),lastcode);
126             }
127         }
128     }
129 }
130 int main()
131 {
132     while(1)
133     {
134         //1、我们自己输入一个命令行
135         Makecommandline();
136 
137         //2、获取用户命令字符串分割
138         char Usercmd[SIZE];
139         Getusercommand(Usercmd);
140         //printf("%s\n",Usercmd);
141         //3、命令行字符串分割
142         Splitcommand(Usercmd);
143         //4、检测命令是否是内建命令
144         int n = Checkbuildin();
145         if(n)continue;
146         //5、执行命令
147         Executecommand();
148     }
149     return 0;
150 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值