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;
}
相关推荐
Empty_7773 小时前
K8S-Job & Cronjob
java·linux·docker·容器·kubernetes
Molesidy4 小时前
【Linux】【Imx6ull Pro】基于Imx6ull Pro开发板的Linux-C-C++编程记录
linux·嵌入式·嵌入式linux应用层
赖small强4 小时前
【Linux 内存管理】Linux系统中CPU访问内存的完整机制深度解析
linux·缓存·tlb·内存访问·page table
赖small强6 小时前
【Linux C/C++开发】Linux 系统野指针崩溃机制深度解析
linux·mmu·crash·core dump·野指针
J__M__C6 小时前
WSL2的环境配置(安装+网络配置+基本美化)
linux
学困昇7 小时前
Linux基础开发工具(下):调试器gdb/cgdb的使用详解
linux·运维·服务器·开发语言·c++
liulilittle7 小时前
Linux shell 搜索指定后缀名文件,并复制到指定目录。
linux·服务器·数据库
必胜刻7 小时前
Redis哨兵模式(Linux)
linux·数据库·redis
阿猿收手吧!8 小时前
【Linux】Ubuntu 24安装webbench
linux·运维·ubuntu