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

Linux自主实现shell

以下是在Linux操作系统 centos7版本下实现的shell ,该shell具备bash的基础功能,无上下键输入历史命令功能,删除字符或命令时按住Ctrl + Back

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<errno.h>
#define COMMAND_SIZE 512
#define ARGV_SIZE 32
#define SPACE " "
#define ZERO '\0'
#define NOMODE 0
#define WRITE_TRUNC 1
#define WRITE_APPEND 2
#define READ 3
#define ENDPATH(p) do{  p += strlen(p)-1; while(*p != '/')p--; }while(0)char cwd[COMMAND_SIZE * 2];
char oldcwd[COMMAND_SIZE * 2];
char* argv[ARGV_SIZE];
int errorcode = 0;
const char* filename;
const char* GetUser()  //获取相应的环境变量内容,下同
{const char* ret = getenv("USER");if(ret == NULL)return "None";return ret;
}const char* GetHostName()
{const char* ret = getenv("HOSTNAME");if(ret == NULL)return "None";return ret;
}const char* GetHome()
{const char* ret = getenv("HOME");if(ret == NULL)return "/";return ret;
}const char* GetPwd()
{const char* ret = getenv("PWD");if(ret == NULL)return "None";return ret;
}const char* GetOldPwd()
{const char* ret = getenv("OLDPWD");if(ret == NULL)return "None";return ret;
}
void ShowUserCommand()
{char commandline[COMMAND_SIZE];  //定义一个缓冲区,用来存放命令行提示符const char* lineuser = GetUser();//获取用户名const char* linehostname = GetHostName(); //获取家目录名称const char* linepwd = GetPwd();  //获取当前工作路径ENDPATH(linepwd);  //得到当前的工作路径后,取出最后一个目录,用宏函数实现linepwd++;linepwd = strlen(linepwd) == 0 ? "/" : (strcmp(linepwd, lineuser) == 0 ? "~" : linepwd)snprintf( commandline, COMMAND_SIZE, "[%s@%s:%s]>> ", lineuser, linehostname, line);// 如果长度为0表示当前工作路径为根目录,打印"/",如果当前工作路径是家目录,打印"~"printf("%s", commandline); //将缓冲区的内容打印到显示器fflush(stdout);
}int SplitCommand( char* tmp)
{//将tmp中的指令切分,读入到命令行参数表argv里int ret = NOMODE;argv[0] = strtok(tmp,SPACE);int i = 1;while((argv[i] = strtok(NULL, SPACE))){//如果指令中含有 ">>", ">","<"等与重定向相关的字符,则设置返回值ret,//对应后续相应的打开文件方式if(ret != NOMODE){i++;continue;}if(strcmp(argv[i], ">>") == 0){ret = WRITE_APPEND;filename = strtok(NULL, SPACE); //若有重定向则更新filename,为后面//的打开文件做准备,下同}else if(argv[i][0] == '>'){if(strlen(argv[i]) == 1){ret = WRITE_TRUNC;filename = strtok(NULL, SPACE);}else if(argv[i][1] == '>'){ret = WRITE_APPEND;filename = argv[i] + 2;}else {ret = WRITE_TRUNC;filename = argv[i] + 1;}}else if(argv[i][0] == '<'){ret = READ;if(strlen(argv[i]) == 1){filename = strtok(NULL, SPACE);}else {filename = argv[i] + 1;}}else {i++;}}argv[i] = NULL;return ret;
}int GetUserCommand(char* usercommand, size_t size)
{//将一整行输入读取到我们定义的缓冲区 usercommand中,进行后续的解析if(NULL == fgets(usercommand, size, stdin)){return 0;}usercommand[strlen(usercommand) - 1] = ZERO;return strlen(usercommand);//返回缓冲区长度,是0表示没有读到指令
}void Die()
{exit(1);
}
void ChangeFile(int mod)
{int fd = 0;if(mod == 0) return ;if(mod == 1 ){fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);  //打开重定向的文件,使用相应的模式打开dup2(fd,1);                                               //将文件描述符为1(标准输出文件)的文件重定向为fd,即我们打开的文件,下同}else if(mod == 2){fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd,1);}else {fd = open(filename, O_RDONLY);dup2(fd,0);}
}
void RunUserCommand(int mod)
{pid_t id = fork();        //执行指令,为子进程运行指令的可执行程序,//父进程为shell程序,不能被影响if(id == 0)               //返回的id为0,为子进程{ChangeFile(mod);        //以mod方式打开文件execvp(argv[0], argv);  //进程替换,替换为要执行的指令exit(errno);}else if(id > 0)           //父进程{int status;if(id == waitpid(id, &status, 0) )  //父进程等待子进程,子进程结束后回收,{                                   //并且记录退出信息statuserrorcode = WEXITSTATUS(status);  //获取退出码if(errorcode != 0)                //错误码不为0,则子进程执行异常,打印异常信息printf("No success:%s:%d\n", strerror(errorcode), errorcode);}}else {Die();                    //创建进程失败,退出shell}
}void Cd()
{const char* path = argv[1];  //用path记录要进入的目录int falg = 0;if(path == NULL ||strcmp(path, "~") == 0 ) path = GetHome();  //如果用户没有指定目录,或者目录为"~"就进入家目录if(strcmp(path,"-") == 0 ){falg = 1;path = GetOldPwd();       //如果用户要进入的目录为 - ,则为最近一次进入的目录,//环境变量oldpwd会记录最近一次进入的目录}char tmp[COMMAND_SIZE * 2];getcwd(tmp,sizeof(tmp)); //将当前工作路径保存,更改路径后用于更新oldpwdif( -1 == chdir(path))   //更改工作路径为path{errorcode =  errno;printf("No success:%s:%d\n", strerror(errorcode), errorcode);return;}snprintf(oldcwd, sizeof(oldcwd), "OLDPWD=%s", tmp);  //在缓冲区更新环境变量OLDPWDgetcwd(tmp,sizeof(tmp));                             //获取更改后的工作路径,//用于更新环境变量PWDsnprintf(cwd, sizeof(cwd),  "PWD=%s", tmp);          //在缓冲区更新环境变量PWDputenv(cwd);                                         //更新环境变量PWDputenv(oldcwd);                                      //更新环境变量OLDPWDif(falg) printf("%s\n", tmp);
}int CheckBuildin(int mod)
{//是内建命令就执行,并且mod不为0时,设置相应的重定向内容int ret = 0;if(strcmp(argv[0],"cd") == 0 ){ret = 1;Cd();}else if(strcmp(argv[0],"exit") == 0 )  //用户输入exit就退出shell程序{ret = 1;exit(0);}else if(strcmp(argv[0], "echo") == 0 && strcmp(argv[1], "$?") == 0 ) //echo $?打印退出码,若有重定向则更换file的指向{ret = 1;FILE* file = stdout;if(mod == 1)file = fopen(filename, "w");else if(mod == 2){file = fopen(filename, "a");}fprintf(file, "%d\n", errorcode);errorcode = 0;                      //更新退出码if(mod != 0)fclose(file);}return ret;
}int main()
{int quit = 1;while(quit){//打印命令行提示符ShowUserCommand();//读取命令行参数char usercommand[ARGV_SIZE * 2];int n = GetUserCommand(usercommand,sizeof(usercommand));if(n < 0) return 1;if(n == 0) continue;//切分命令行参数int mod = SplitCommand(usercommand);//判断是否是内建命令n = CheckBuildin(mod);//执行命令if(n == 0)RunUserCommand(mod);}return 0;
}
http://www.lryc.cn/news/608138.html

相关文章:

  • C#开发入门指南_学习笔记
  • Ubuntu系统VScode实现opencv(c++)图像翻转和旋转
  • Java 注解详解(含底层原理)
  • Vue 3.0 Composition API:重新定义组件逻辑的组织方式
  • 算法训练营DAY46 第九章 动态规划part13
  • 全球化 2.0 | 中国香港教育机构通过云轴科技ZStack实现VMware替代
  • stm32103如果不用32k晶振,那引脚是悬空还是接地
  • SLAM中的非线性优化-2D图优化之零空间实战(十六)
  • Linux iptables防火墙操作
  • Apache Doris数据库——大数据技术
  • SpringBoot怎么查看服务端的日志
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博舆情数据可视化分析-热词情感趋势树形图
  • sqli-labs:Less-21关卡详细解析
  • 【BTC】挖矿难度调整
  • 人类学家与建筑师:区分UX研究和项目管理的需求分析
  • 隧道照明“隐形革命”:智能控制如何破解安全与节能双重命题
  • 【iOS】strong和copy工作流程探寻、OC属性关键字复习
  • 电脑手机热点方式通信(下)
  • 「iOS」————weak底层原理
  • 「iOS」————SideTable
  • JAVA国际版同城服务同城信息同城任务发布平台APP源码Android + IOS
  • Ajax——异步前后端交互提升OA系统性能体验
  • Dice Combinations(Dynamic Programming)
  • 8.2 状态机|贪心|dfs_dp
  • Linux初步认识与指令与权限
  • 机器学习——K 折交叉验证(K-Fold Cross Validation),实战案例:寻找逻辑回归最佳惩罚因子C
  • Jotai:React轻量级原子化状态管理,告别重渲染困扰
  • React ahooks——副作用类hooks之useThrottleFn
  • react 和 react native 的开发过程区别
  • Javascript面试题及详细答案150道之(016-030)