【linux】自主shell编写

Alt

🔥个人主页Quitecoder

🔥专栏linux笔记仓

Alt

01.输出命令行

完成对一个shell 的编写,首先我们需要输出一个自己的命令行

在这里插入图片描述
我们可以通过getenv来获取环境变量

const char * GetSserName()
{
     const char *name = getenv("USER");
     if(name == NULL) return "None";
     return name;
}

用getnev来获取USER

在这里插入图片描述
拿到用户名后,第二个获取主机名

const char * GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname==NULL) return "None";
    return hostname;
}

接着我们获取路路径,这里先写一个不规范的路径版本:

const char * GetCwd()
{
    const char *cwd = getenv("PWD");
    if(cwd == NULL ) return "None";
    return cwd;
}

在这里插入图片描述

后面我们再对路径进行截取

在这里插入图片描述
现在完成对命令行输出的编写,我们目标是将变量名放到一个输出型参数commandline中,这里需要一个函数snprintf:
在这里插入图片描述

void MakeCommandLine(char line[],size_t size)
{
    const char* username= GetUserName();
    const char* hostname= GetHostName();
    const char* cwd= GetCwd();
    snprintf(line,size,"[%s@%s %s]> ",username,hostname,cwd);
    printf("%s",line);
    fflush(stdout);
}

这就完成了命令行输出部分的函数,这里打印是向缓冲区打印,我们需要刷新缓冲区

02.获取用户命令字符串

用户输入的各种指令,本质就是一个字符串,我们要做的就是对字符串进行截取并且按照要求完成内容输出

我们这里不能直接用scanf来获取,因为这里scanf的分隔符为空格,我们这里想按照行来拿字符串,用fgets

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们这里将输入的回车\n改为\0;

在这里插入图片描述
我们向usercommand这个缓冲区输入来获取命令

03.命令行字符串分割

在这里插入图片描述

这里定义全局的存储各个命令的字符串数组,用strtok进行分割,注意!:这里strtok的第二个参数是const char *类型的,一定是一个字符串
在这里插入图片描述
所以我们这里定义的分隔符必须是字符串

在这里插入图片描述
这里写成=,表示先赋值,再判断,分割之后,strtok会返回NULL,刚好让gArgv最后一个元素是NULL,并且while判断结束

检验结果:
在这里插入图片描述

04.执行命令

执行命令,我们创建子进程进行程序替换

在这里插入图片描述
在这里插入图片描述
我们将上面的代码放入一个新函数中,并让上面的过程持续进行:

在这里插入图片描述
在这里插入图片描述

05.细节修改

在这里插入图片描述

我们发现现在,执行cd命令是没有反应的

自定义 shell 无法运行 cd 指令的原因主要是因为 cd 是一个 内建命令,它不会创建新进程,而是直接改变当前进程的工作目录。因此,简单地使用 fork()execvp() 来执行 cd 是不行的,因为 cd在子进程内生效,但子进程在执行完命令后会终止,所以父进程的工作目录不会改变。

在当前的代码中,所有的命令都会通过 fork() 创建子进程,并在子进程内使用 execvp() 执行。这种方法适用于外部命令,但对 cd 这样的内建命令并不适用


要让 cd 命令能够正确工作,需要在父进程中执行 cd 操作,而不是在子进程中。可以通过检查用户输入的命令是否为 cd,如果是 cd,则在父进程中直接使用 chdir() 系统调用来改变当前工作目录。

检查是否为内建命令

const char * GetHome()
{
    const char *home=getenv("HOME");
    if(home== NULL) return "/root";
    return home;
}

void Cd()
{
    const char *path=gArgv[1];
    if(path==NULL)path =GetHome();
    chdir(path);

}
int CheckBuildin()
{
    int yes=0;
    const char * enter_cmd= gArgv[0];
    if(strcmp("cd",enter_cmd)==0)
    {
        yes=1;
        Cd();
    }

    return yes;
}
int main()
{
    int quit=0;
  while(!quit)
  {
    //1.输出命令行
    MakeCommandLine();

    //2.获取输入命令

    char usercommand[SIZE];
    int n=  GetUserCommand(usercommand,sizeof(usercommand));
    if(n<=0) return 1;
    //3.命令行字符串分割
    SplitCommand(usercommand,sizeof(usercommand));  
    //4.检查是否为内建命令;
    n=CheckBuildin();
    if(n)continue;
    //执行命令
    ExecuteCommand();
  }
  return 0;

}

在这里插入图片描述
现在cd命令可以使用,但是环境变量还是有问题,还需要修改

char cwd[SIZE*2];void Cd()
{
    const char *path=gArgv[1];
    if(path==NULL)path =GetHome();
    chdir(path);
    snprintf(cwd,sizeof(cwd),"PWD=%s",path);

    putenv(cwd);
}
int CheckBuildin()
{
    int yes=0;
    const char * enter_cmd= gArgv[0];
    if(strcmp("cd",enter_cmd)==0)
    {
        yes=1;
        Cd();
    }
    return yes;
}

在这里插入图片描述

void Cd()
{
    const char *path=gArgv[1];
    if(path==NULL)path =GetHome();
    chdir(path);
    //刷新环境变量
    char temp[SIZE*2];
    getcwd(temp,sizeof(temp));
    snprintf(cwd,sizeof(cwd),"PWD=%s",temp);                                                                                    
    putenv(cwd);                     
}   

在这里插入图片描述
还需要更改的是,系统的shell只会显示当前路径,而我们自定义的shell会显示绝对路径

在这里插入图片描述

#define SkipPath(p) do{ p+= strlen(p)-1;   while(*p!='/')p--;\
}while(0)

void MakeCommandLine()
{
    char line[SIZE];
    const char* username= GetUserName();
    const char* hostname= GetHostName();
    const char* cwd= GetCwd();

    SkipPath(cwd);
    snprintf(line,sizeof(line),"[%s@%s %s]> ",username,hostname,strlen(cwd)==1?"/":cwd+1);
    printf("%s",line);
    fflush(stdout);
}

在这里插入图片描述
最后加上退出码

int lastnode=0;
void ExecuteCommand()
{

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

int CheckBuildin()                                                               
{                                                                                
    int yes=0;                                                                   
    const char * enter_cmd= gArgv[0];                                            
    if(strcmp("cd",enter_cmd)==0)                                                
    {                                                                            
        yes=1;                                                                   
        Cd();                                                                    
    }                                                                            
    else if(strcmp(enter_cmd,"echo")==0&&strcmp(gArgv[1],"$?")==0)               
    {                                                                            
        printf("%d\n",lastcode);                                   
        lastcode=0;                                                                                                             
    }                                                      
                                                           
    return yes;                                            
}  

在这里插入图片描述

完整代码:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{ p+= strlen(p)-1;   while(*p!='/')p--;\
}while(0)

char cwd[SIZE*2];
char *gArgv[NUM];

int lastcode=0;

const char * GetUserName()
{
     const char *name = getenv("USER");
     if(name == NULL) return "None";
     return name;
}
const char * GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname==NULL) return "None";
    return hostname;
}
const char * GetCwd()
{
    const char *cwd = getenv("PWD");
    if(cwd == NULL ) return "None";
    return cwd;
}
const char * GetHome()
{
    const char *home=getenv("HOME");
    if(home== NULL) return "/root";
    return home;
}
void MakeCommandLine()
{
    char line[SIZE];
    const char* username= GetUserName();
    const char* hostname= GetHostName();
    const char* cwd= GetCwd();

    SkipPath(cwd);
    snprintf(line,sizeof(line),"[%s@%s %s]> ",username,hostname,strlen(cwd)==1?"/":cwd+1);
    printf("%s",line);
    fflush(stdout);
}
int GetUserCommand(char command[],size_t n)
{
  char *s =fgets(command,n,stdin);
  if(s==NULL) return 1; 
  command[strlen(command)-1]=ZERO;
  return strlen(command);
}

void SplitCommand(char command[],size_t size)
{
   gArgv[0]=strtok(command,SEP);
   int index=1;
   while((gArgv[index++]=strtok(NULL,SEP)));
   (void)size;
}
void ExecuteCommand()
{

  pid_t id=fork();
  if(id < 0)exit(1);
  else if(id == 0)
  {
      execvp(gArgv[0],gArgv);
      exit(errno);
  }
  else 
  {
      int status=0;
      pid_t rid = waitpid(id,&status,0);
      if(rid>0)
      {
        lastcode=WEXITSTATUS(status);
        if(lastcode!=0) printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
      }
  }
}
void Cd()
{
    const char *path=gArgv[1];
    if(path==NULL)path =GetHome();
    chdir(path);
    //刷新环境变量
    char temp[SIZE*2];
    getcwd(temp,sizeof(temp));
    snprintf(cwd,sizeof(cwd),"PWD=%s",temp);
    putenv(cwd);
}
int CheckBuildin()
{
    int yes=0;
    const char * enter_cmd= gArgv[0];
    if(strcmp("cd",enter_cmd)==0)
    {
        yes=1;
        Cd();
    }
    else if(strcmp(enter_cmd,"echo")==0&&strcmp(gArgv[1],"$?")==0)
    {
        yes=1;
        printf("%d\n",lastcode);
        lastcode=0;
    }

    return yes;
}
int main()
{
    int quit=0;
  while(!quit)
  {
    //1.输出命令行
    MakeCommandLine();

    //2.获取输入命令

    char usercommand[SIZE];
    int n=  GetUserCommand(usercommand,sizeof(usercommand));
    if(n<=0) return 1;
    //3.命令行字符串分割
    SplitCommand(usercommand,sizeof(usercommand));  
    //4.检查是否为内建命令;
    n=CheckBuildin();
    if(n)continue;
    //执行命令
    ExecuteCommand();
  }
  return 0;

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QuiteCoder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值