【Linux 小实战】自定义 Shell 的编写

文章目录

1.输出自己的命令行

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define SIZE 256

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();

  snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);
  printf("%s", line);
  fflush(stdout);
}


int main()
{
  //1.输出自己的命令行
  MakeCommandLineAndPrint();
  sleep(5); //用来测试 MakeCommandLinePrint函数中fflush刷新缓冲区的作用

  return 0;
}

2.获取用户命令字符串

  • 使用 char *fgets(char *s, int size, FILE *stream); 库函数
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

#define SIZE 256
#define ZERO '\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;
}

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

  snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);
  printf("%s", line);
  fflush(stdout);
}

int GetUserCommand(char command[], size_t n)
{
  char* s = fgets(command, n, stdin);

  if (s == NULL)  return -1;

  //目的是为了解决 "\n"被都进来的情况
  command[strlen(command) - 1] = ZERO;

  return strlen(command);
}

int main()
{
  //1.输出自己的命令行
  MakeCommandLineAndPrint();

  //2.获取用户输入的命令字符串
  char usercommand[SIZE];
  int n = GetUserCommand(usercommand, sizeof(usercommand));

  printf("%s\n", usercommand);

  return 0;
}

3.分割命令行字符串

》》 strtok() 分割的使用

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

#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32


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();

  snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);
  printf("%s", line);
  fflush(stdout);
}

int GetUserCommand(char command[], size_t n)
{
  char* s = fgets(command, n, stdin);

  if (s == NULL)  return -1;

  //目的是为了解决 "\n"被都进来的情况
    command[strlen(command) - 1] = ZERO;

  return strlen(command);
}

char* gArgv[NUM];

void SplitCommand(char command[], size_t n)
{
  gArgv[0] = strtok(command, SEP);

  int index = 1;
  while(gArgv[index++] = strtok(NULL, SEP));

}

int main()
{
  //1.输出自己的命令行
  MakeCommandLineAndPrint();

  //2.获取用户输入的命令字符串
  char usercommand[SIZE];
  int n = GetUserCommand(usercommand, sizeof(usercommand));

  //3.命令行字符串分割
  SplitCommand(usercommand, sizeof(usercommand));

  for (int i = 0; gArgv[i]; i ++)
    printf("gArgv[%d]:%s\n",i, gArgv[i]);

  return 0;
}

4.开始执行,第一版本Shell

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

#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32


void Die()
{
  exit(1);
}

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();

  snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);
  printf("%s", line);
  fflush(stdout);
}

int GetUserCommand(char command[], size_t n)
{
  char* s = fgets(command, n, stdin);

  if (s == NULL)  return -1;

  //目的是为了解决 "\n"被都进来的情况
  command[strlen(command) - 1] = ZERO;

  return strlen(command);
}


char* gArgv[NUM];

void SplitCommand(char command[], size_t n)
{
  gArgv[0] = strtok(command, SEP);

  int index = 1;
  while(gArgv[index++] = strtok(NULL, SEP));

}


void ExecuteCommand()
{
  pid_t id = fork();
  if (id < 0) Die();

  //child
  if (id == 0)
  {
    execvp(gArgv[0], gArgv);
    exit(errno);
  }
  else
  {
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
  }
}


int main()
{
  int quit = 0;
  while (!quit)
  {
          //1.输出自己的命令行
          MakeCommandLineAndPrint();

          //2.获取用户输入的命令字符串
 		  char usercommand[SIZE];
          int n = GetUserCommand(usercommand, sizeof(usercommand));

          //3.命令行字符串分割
          SplitCommand(usercommand, sizeof(usercommand));

          //4.执行命令
          ExecuteCommand();

  }
  return 0;
}

5.检查内键命令

  • 第一版本Shell存在的问题,cd 无效,工作的路径根本没有改变,原因是由于 cd属于内建命令,而内建命令是要由父进程Bash来执行的,下面出现Bug的原因就是由于子进程执行的cd命令;

  • 🍊处理内建命令 cd的主要核心代码如下图:
    代码解释: char *getcwd(char *buf, size_t size); 获取当前的工作目录
cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32

char cwd[SIZE*4];

void Die()
{
  exit(1);
}

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();

  snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);
  printf("%s", line);
  fflush(stdout);
}

int GetUserCommand(char command[], size_t n)
{
  char* s = fgets(command, n, stdin);

  if (s == NULL)  return -1;

  //目的是为了解决 "\n"被都进来的情况
  command[strlen(command) - 1] = ZERO;

  return strlen(command);
}

char* gArgv[NUM];

void SplitCommand(char command[], size_t n)
{
  gArgv[0] = strtok(command, SEP);

  int index = 1;
  while(gArgv[index++] = strtok(NULL, SEP));

}

void ExecuteCommand()
{
  pid_t id = fork();
  if (id < 0) Die();

  //child
  if (id == 0)
  {
    execvp(gArgv[0], gArgv);
    exit(errno);
  }
  else
  {
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
  }
}

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"))
  {
    yes = 1;
    Cd();
  }
  return yes;
}

int main()
{
  int quit = 0;
  while (!quit)
  {
          //1.输出自己的命令行
          MakeCommandLineAndPrint();

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

          //5.执行命令
          ExecuteCommand();
  }
  return 0;
}
相关推荐
2301_7890156210 分钟前
Lnux权限
linux·开发语言·c++·权限
楚枫默寒8 小时前
Linux 编辑文件后自动添加修改日期
linux·运维·bash
2601_9611940210 小时前
27考研刘晓艳单词pdf
linux·sql·ubuntu·华为·pdf·.net
2023自学中12 小时前
imx6ull 开发板 推流ov5640数据,虚拟机用 ffplay 拉流播放
linux·音视频·嵌入式·开发板
shandianchengzi12 小时前
【记录】Ghidra|Ubuntu 26.04 下 Ghidra 界面缩放完整指南
linux·ubuntu·逆向·ghidra
Soari12 小时前
Ubuntu 根分区文件系统损坏,系统启动时自动检查失败
linux·运维·ubuntu
杨云龙UP12 小时前
Oracle Health Check巡检脚本使用SOP V2.0:从HTML原始报告→生成Word专业巡检报告→交付客户_2026-06-03
linux·运维·数据库·sql·oracle·报告·巡检
广州灵眸科技有限公司12 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) Linux虚拟机准备
linux·运维·服务器
Lana学习中13 小时前
【运维杂记】连接不上远程服务器的问题处理
运维·服务器
1892280486113 小时前
NV023固态MT29F16T08GWLCEJ9-QBES:C
大数据·服务器·人工智能·科技·缓存