首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【linux】自主shell编写

【linux】自主shell编写

作者头像
用户11029103
发布2025-02-25 13:48:18
发布2025-02-25 13:48:18
8490
举报
文章被收录于专栏:技术学习技术学习

01.输出命令行

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

我们可以通过getenv来获取环境变量

代码语言:javascript
复制
const char * GetSserName()
{
     const char *name = getenv("USER");
     if(name == NULL) return "None";
     return name;
}

用getnev来获取USER

拿到用户名后,第二个获取主机名

代码语言:javascript
复制
const char * GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname==NULL) return "None";
    return hostname;
}

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

代码语言:javascript
复制
const char * GetCwd()
{
    const char *cwd = getenv("PWD");
    if(cwd == NULL ) return "None";
    return cwd;
}

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

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

代码语言:javascript
复制
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() 系统调用来改变当前工作目录。

检查是否为内建命令
代码语言:javascript
复制
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命令可以使用,但是环境变量还是有问题,还需要修改

代码语言:javascript
复制
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;
}
代码语言:javascript
复制
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会显示绝对路径

代码语言:javascript
复制
#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);
}

最后加上退出码

代码语言:javascript
复制
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;                                            
}  

完整代码:

代码语言:javascript
复制
#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;

}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01.输出命令行
  • 02.获取用户命令字符串
  • 03.命令行字符串分割
  • 04.执行命令
  • 05.细节修改
    • 检查是否为内建命令
  • 完整代码:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档