当前位置: 首页 > news >正文

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

文章目录

      • 1.输出自己的命令行
      • 2.获取用户命令字符串
      • 3.分割命令行字符串
      • 4.开始执行,第一版本Shell
      • 5.检查内键命令

1.输出自己的命令行

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#define SIZE 256const 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); 库函数
#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() 分割的使用
在这里插入图片描述

#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 32const 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

#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 32void 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();//childif (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); 获取当前的工作目录

在这里插入图片描述

#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 32char 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();//childif (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;
}
http://www.dtcms.com/a/353662.html

相关文章:

  • LCD 上显示字符
  • zookeeper-集群扩缩容
  • 稳敏双态融合架构--架构师的练就
  • banner这个文件是怎么请求到后端数据的
  • Rust:引用
  • Vue-24-利用Vue3的element-plus库实现树形结构数据展示
  • Autodesk Maya 2026.2 全新功能详解:MotionMaker AI 动画、LookdevX 材质增强、USD 工作流优化
  • 在MiniOB源码中学习使用Flex与Bison解析SQL语句-第二节
  • 【Linux】正则表达式学习记录
  • FFMPEG api使用
  • 从disable_cost到disabled_nodes,最小代价预估质的飞跃
  • nestjs日志(nest-winston)
  • pyecharts可视化图表-tree:从入门到精通
  • Linux 系统调优与CPU-IO-网络内核参数调优
  • Task04: CAMEL框架中的多智能体系统(课程第三章剩余章节)
  • 大模型安全概述、LlamaFirewall
  • ESP8266:Arduino学习
  • 前端性能优化:从指标监控到全链路落地(2024最新实战指南)
  • 短视频矩阵管理软件推荐——小麦矩阵系统深度解析
  • 关于两视图相机几何关系
  • DevExpress WPF中文教程:如何将WPF数据网格绑定到本地集合?
  • 软件定义汽车(SDV)调试——如何做到 适配软件定义汽车(SDV)?(下)
  • vue新能源汽车销售平台的设计与实现(代码+数据库+LW)
  • 【Vue2✨】 Vue2 入门之旅(二):模板语法
  • Python异步编程:从理论到实战的完整指南
  • Qt---项目架构解读
  • BiLSTM-Attention分类预测+SHAP分析+特征依赖图!深度学习可解释分析,Matlab代码实现
  • 【GaussDB】深度解析:创建存储过程卡死且无法Kill会话的疑难排查
  • codeforces(1045)(div2)D. Sliding Tree
  • 装饰器模式(C++python)