【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;
}
相关推荐
用户31187945592184 小时前
Kylin Linux 10 安装 glib2-devel-2.62.5-7.ky10.x86_64.rpm 方法(附安装包)
linux
涛啊涛4 小时前
Centos7非LVM根分区容量不足后扩容,对调硬盘挂载/
linux·磁盘管理
CYRUS_STUDIO1 天前
用 Frida 控制 Android 线程:kill 命令、挂起与恢复全解析
android·linux·逆向
熊猫李1 天前
rootfs-根文件系统详解
linux
dessler1 天前
Hadoop HDFS-高可用集群部署
linux·运维·hdfs
泽泽爱旅行1 天前
awk 语法解析-前端学习
linux·前端
轻松Ai享生活2 天前
5 节课深入学习Linux Cgroups
linux
christine-rr2 天前
linux常用命令(4)——压缩命令
linux·服务器·redis
三坛海会大神5552 天前
LVS与Keepalived详解(二)LVS负载均衡实现实操
linux·负载均衡·lvs
東雪蓮☆2 天前
深入理解 LVS-DR 模式与 Keepalived 高可用集群
linux·运维·服务器·lvs