Linux:自主shell编写

打印命令行

首先我们需要一个函数打印一下命令行:

整体框架是这样,现在我们要分别实现三个Get函数:

我们通过getenv函数获取bash给的环境变量

注意到此时我们的getcwd给的是整个路径名,而我们只需要最后的文件夹名,因此需要处理一下:

为了避免使用二重指针作为参数,我们可以定义一个宏函数。

获取用户命令

接下里我们要获取用户输入的命令,事实上这非常的简单:

注意我们这里要将用户输入的回车键去掉。

分割命令

我们现在获取的是一个字符串,具体的指令和选项以空格分开,因此我们需要具体地分割字符串:

其中gArgv定义为全局变量即可,SEP分隔符为" ".

检测是否为内建命令


由于内建命令和环境变量过多,我们这里就实现cd和$?

其中lastcode定义为全局变量。

其中chdir、getcwd和putenv都是库函数。因为改变路径后我们还需获取当前的绝对路径,然后对环境变量进行修改。

执行命令

最后就是执行命令了:

我们当然不能直接执行命令,而是交给子进程执行!

最后调整一下main函数:

完整代码

c 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

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

char cwd[SIZE*2];
char *gArgv[NUM];
int lastcode=0;

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

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;
}

void MakeCommandLineAndPrint()
{
  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]='\0';
  return strlen(command);
}

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

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(enter_cmd,"cd")==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;
}

void ExecuteCommand()
{
  pid_t id=fork();
  if(id<0)exit(1);
  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 main()
{
  int quit=0;
  while(!quit)
  {
    MakeCommandLineAndPrint();

    char usercommand[SIZE];
    int n=GetUserCommand(usercommand,sizeof(usercommand));
    if(n<0)return 1;

    SplitCommand(usercommand,sizeof(usercommand));
    
    n=CheckBuildin();
    if(n)continue;

    ExecuteCommand();
  }
  return 0;
}
相关推荐
s_daqing12 小时前
ubuntu(arm)安装redis
linux·redis·ubuntu
林鸿群12 小时前
ubuntu 26.04 安装mysql-server
linux·mysql·ubuntu
betazhou12 小时前
rsync使用案例分析
linux·运维·服务器·rsync·同步数据
安静的技术开发者12 小时前
Linux Ubuntu学习笔记
linux·ubuntu
geshifei13 小时前
Sched ext回调1——init_task (linux 6.15.7)
linux·ebpf
晚风予卿云月13 小时前
【Linux】自动化构建—make/Makefile
linux·自动化·make/makefile
脑子进水养啥鱼?14 小时前
Linux find 命令
linux·运维
梓䈑14 小时前
【Linux系统】实现线程池项目(含日志类的设计)
linux·服务器·c++
EverydayJoy^v^14 小时前
RH124简单知识点——第8章——配置和保护SSH
linux·运维·ssh
取加若则_14 小时前
Linux环境变量与命令行参数全解析
linux