wordpress 做手机站,您的网站对百度设置了ip封禁,做网站用广告赚钱过时了,手机软件开发商[Linux]编写一个极简版的shell-version1 文章目录 [Linux]编写一个极简版的shell-version1命令行提示符打印接收命令行参数将命令行参数进行解释执行用户命令完整代码 本文能够帮助Linux系统学习者通过代码的角度更好地理解命令行解释器的实现原理。 命令行提示符打印
Linux操…[Linux]编写一个极简版的shell-version1 文章目录 [Linux]编写一个极简版的shell-version1命令行提示符打印接收命令行参数将命令行参数进行解释执行用户命令完整代码 本文能够帮助Linux系统学习者通过代码的角度更好地理解命令行解释器的实现原理。 命令行提示符打印
Linux操作系统运行时就需要shell进程进行命令行解释然后让系统完成对应的命令因此打印命令行提示符时要采用死循环打印的方式具体的代码逻辑如下
int main()
{while(1){printf([%s%s %s]$$ , getenv(USER), getenv(HOSTNAME), getpath(getenv(PWD)));fflush(stdout);//sleep(200); -- 当前阶段用于演示效果}为了和系统的shell进行区分因此打印两个$符号命令行提示符打印的信息都是通过环境变量得来的因此只需要调用系统接口获取相应的环境变量即可 USER环境变量 – 当前使用的用户名HOSTNAME环境变量 – 当前的主机名称PWD环境变量 – 当前用户所处绝对路径 由于显示器采用的是行缓冲的策略因此需要手动刷新缓冲区才能让命令行提示符显式到屏幕上
由于环境变量PWD是当前用户所处的绝对路径因此需要编写一个函数getpath来获取当前所处的目录名称具体的代码逻辑如下
const char* getpath(char* path)
{int length strlen(path);if(length 1) return /; //根目录int i length - 1;while((path[i] ! /)) i--;return pathi1;
}如果绝对路径的字符串长度为1表示所处为根目录返回/其余路径下都要将绝对路径里当前目录前的所有路径分割掉包括路径分隔符譬如当前记录绝对路径环境变量为PWD/home/qxm/linux-warehouse/review/mybash/version1getpath的返回值为verson1字符串的首地址
效果演示 接收命令行参数
接受命令行参数只需要设置一个字符串数组将用户的输入接收即可具体的代码逻辑如下
#define MAX 1024int main()
{while(1){char commandstr[MAX] { 0 }; //接收命令行参数printf([%s%s %s]$$ , getenv(USER), getenv(HOSTNAME), getpath(getenv(PWD)));fflush(stdout);char* s fgets(commandstr, sizeof(commandstr), stdin);commandstr[strlen(commandstr) - 1] \0; //去除输入时末尾的\n//printf(%s\n, commandstr); -- 当前阶段用于演示效果}return 0;
}由于用户输入时会按下回车也就是会在输入时在末尾出现一个\n,命令行参数使用时需要将其去除掉譬如用户输入ls -a -l\n,命令行参数只需要ls -a -l
效果演示 将命令行参数进行解释
获取用户输入的命令行参数后需要将命令行参数根据输入的空格将字符串分割为多个字串譬如用户输入ls -a -l,要分割为ls, -a, -l,具体的代码逻辑如下
#define MAX 1024
#define ARGC 64
#define SEP int split(char commandstr[], char* argv[])//命令行参数解释
{argv[0] strtok(commandstr, SEP);if (argv[0] NULL) return -1; //字符串为空int i 1;while(1){argv[i] strtok(NULL, SEP);if(argv[i] NULL) break;i;}return 0;
}void debugPrint(char* argv[])//--当前阶段用于演示效果
{int i 0;for (i 0; argv[i] ! NULL; i){printf(%s\n, argv[i]);}
}int main()
{while(1){char commandstr[MAX] { 0 }; //接收命令行参数char* argv[ARGC] { NULL }; //存储命令行参数printf([%s%s %s]$$ , getenv(USER), getenv(HOSTNAME), getpath(getenv(PWD)));fflush(stdout);char* s fgets(commandstr, sizeof(commandstr), stdin);commandstr[strlen(commandstr) - 1] \0; //去除输入时末尾的\nint n split(commandstr, argv);if(n ! 0) continue; //用户输入空串//debugPrint(argv); -- 当前阶段用于演示效果}return 0;
}调用C语言库函数strtok获取分割空格字符后字串的首地址并且将空格置为\0并将字串的首地址存储起来
效果演示 执行用户命令
创建子进程进程进程程序替换让子进程完成用户所输入的命令具体的代码逻辑如下
#define MAX 1024
#define ARGC 64
#define SEP const char* getpath(char* path)
{int length strlen(path);if(length 1) return /; //根目录int i length - 1;while((path[i] ! /)) i--;return pathi1;
}int split(char commandstr[], char* argv[])
{argv[0] strtok(commandstr, SEP);if (argv[0] NULL) return -1; //字符串为空int i 1;while(1){argv[i] strtok(NULL, SEP);if(argv[i] NULL) break;i;}return 0;
}int main()
{while(1){char commandstr[MAX] { 0 }; //接收命令行参数char* argv[ARGC] { NULL }; //存储命令行参数printf([%s%s %s]$$ , getenv(USER), getenv(HOSTNAME), getpath(getenv(PWD)));fflush(stdout);char* s fgets(commandstr, sizeof(commandstr), stdin);commandstr[strlen(commandstr) - 1] \0; //去除输入时末尾的\nint n split(commandstr, argv);if(n ! 0) continue; //用户输入空串pid_t id fork();//创建子进程完成命令执行if (id 0){//子进程execvp(argv[0], argv); //进程程序替换exit(0);}int status 0;waitpid(id, status, 0);//回收子进程}return 0;
}由于实现的shell用于执行系统命令并且获取了记录命令行参数的数组因此采用execvp进程程序替换
效果演示 完整代码
#include stdio.h
#include stdlib.h
#include string.h
#include assert.h
#include unistd.h
#include sys/types.h
#include sys/wait.h#define MAX 1024
#define ARGC 64
#define SEP const char* getpath(char* path)
{int length strlen(path);if(length 1) return /; //根目录int i length - 1;while((path[i] ! /)) i--;return pathi1;
}int split(char commandstr[], char* argv[])
{assert(commandstr);assert(argv);argv[0] strtok(commandstr, SEP);if (argv[0] NULL) return -1; //字符串为空int i 1;while(1){argv[i] strtok(NULL, SEP);if(argv[i] NULL) break;i;}return 0;
}int main()
{while(1){char commandstr[MAX] { 0 }; //接收命令行参数char* argv[ARGC] { NULL }; //存储命令行参数printf([%s%s %s]$$ , getenv(USER), getenv(HOSTNAME), getpath(getenv(PWD)));fflush(stdout);char* s fgets(commandstr, sizeof(commandstr), stdin);assert(s); //对fgets函数的结果断言(void)s;//保证在release方式发布的时候因为去掉assert了所以s就没有被使用而带来的编译告警, 什么都没做但是充当一次使用commandstr[strlen(commandstr) - 1] \0; //去除输入时末尾的\nint n split(commandstr, argv);if(n ! 0) continue; //用户输入空串pid_t id fork();assert(id 0);(void)id;if (id 0){//子进程execvp(argv[0], argv); exit(0);} int status 0;waitpid(id, status, 0);}return 0;
}
);if(n ! 0) continue; //用户输入空串pid_t id fork();assert(id 0);(void)id;if (id 0){//子进程execvp(argv[0], argv); exit(0);} int status 0;waitpid(id, status, 0);}return 0;
}说明 该版本shell只能够执行系统命令并且不能够支持所有系统命令比如cd命令。