网站正在升级建设中购物网站 后台

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

网站正在升级建设中,购物网站 后台,利用ionic做的网站,网络规划设计师的意义本文旨在通过自己完成一个简单的Shell来帮助理解命令行Shell这个程序。 目录 一、输出“提示” 二、获取输入 三、切割字符串 四、执行指令 1.子进程替换 2.内建指令 一、输出“提示” 这个项目基于虚拟机Ubuntu22.04.5实现。 打开终端界面如图所示。 其中。 之前#x…         本文旨在通过自己完成一个简单的Shell来帮助理解命令行Shell这个程序。 目录 一、输出“提示” 二、获取输入 三、切割字符串 四、执行指令 1.子进程替换 2.内建指令 一、输出“提示” 这个项目基于虚拟机Ubuntu22.04.5实现。 打开终端界面如图所示。 其中。 之前utocoo是用户名 之后utocoo-virtul-machine是主机名 :之后是当前路径~表示用户家目录 \(是普通用户的提示符如果是root用户则为# 光标闪烁位置在等待输入 当前的用户名主机名当前工作目录这些信息都有对应的环境变量故可以利用getenv拿到对应的值。 #include stdio.h #include stdlib.h //获取用户名 const char* UserName() {const char* username getenv(USER);if(username)return username;elsereturn None; } //获取主机名 const char* HostName() {const char* hostname getenv(HOSTNAME);if(hostname)return hostname;elsereturn None; } //获取目录 const char* CurrentWorkDir() {const char* cwd getenv(PWD);if(cwd)return cwd;else return None; } int main() {printf(%s%s:%s\),UserName(),HostName(),CurrentWorkDir());return 0; }二、获取输入 用户的输入是作为一个字符串被输入故需要定义一个数组作为缓冲区。 使用scanf输入时遇到空格则会刷新缓冲区故推荐使用fgets函数作为输入函数。关于C语言的各组输入函数这篇文章做了很好的说明。https://blog.csdn.net/qq_53139964/article/details/142820767  补全其他板块的代码完成“获取输入”这一步骤。 #include stdio.h #include stdlib.h#define SIZE 1024 //定义缓冲区数组大小const char* HostName() {const char* hostname getenv(HOSTNAME);if(hostname)return hostname;elsereturn None; } const char* UserName() {const char* username getenv(USER);if(username)return username;elsereturn None; } const char* CurrentWorkDir() {const char* cwd getenv(PWD);if(cwd)return cwd;else return None; } int main() {char commandline[SIZE];printf(%s%s:%s\( ,UserName(),HostName(),CurrentWorkDir());//获取用户输入fgets(commandline,SIZE,stdin);printf(test:%s\n,commandline);return 0; }测试结果如下。 不难看出打印结果中有两次“换行操作”。原因是fgets在stdin流中读取一定数量的信息时会将我们自己输入的\n也读取进来因此需要在commandline数组中去掉这个字符。 commandline[strlen(commandline)-1] 0;//将\n修改为0封装处理后。 //命令行交互 void Interactive(char* out,int size) { printf(%s%s:%s\) ,UserName(),HostName(),CurrentWorkDir());//获取用户输入fgets(out,size,stdin);out[strlen(out)-1] 0;//将\n修改为0 }三、切割字符串 我们知道命令行也是正在运行的程序而在命令行执行输入的指令其实是命令行这个进程创建子程序后再做程序替换注意程序替换时传参方式要么是可变参数要么是指针数组因此无论如何都要先将当前的字符串按照空格切割成一个个的子串如果是指针数组的形式要以NULL结尾。 切割字符串可以利用C语言的字符串处理函数strtok。 #define MAX_ARGC 64 #define SPC
char* argv[MAX_ARGC];//切割字符串 void Split(char* in) {int i 0;argv[i] strtok(in,SPC);while(argv[i] strtok(NULL,SPC)); }在这里切割字符串的语句是这样一句while循环循环体为空。仅仅这样一行代码就可以实现我们对字符串切割的要求因为argv数组要求要以NULL结尾而这句赋值语句将最后一个字符\0赋值给数组元素后数组尾的数据就是NULL同时表达式的值也为假跳出循环。 四、执行指令 1.子进程替换 我们当前做的所有工作都是模拟shell这个程序而模拟的命令行要执行我们输入的指令必然要通过程序替换来完成但是不能用shell这个进程做替换应该创建子进程让子进程做程序替换父进程等待子进程。 #include unistd.h #include sys/types.h #include sys/wait.h//3.执行命令 pid_t id fork(); if(id 0) {//程序替换子进程执行命令exit(1); }pid_t rid waitpid(id,Null,0); printf(run done!:%d,rid); return 0;程序替换时选择适当的替换函数也是很重要的我们在模拟的时候是创建了argv数组故选择带v的exec函数其次带p的exec函数可以不用指定系统指令的全部路径故选择execvp这个函数。 //执行指令 void Execute() {pid_t id fork();if(id 0){//程序替换子进程执行命令execvp(argv[0],argv);exit(1);}pid_t rid waitpid(id,NULL,0);printf(run done!:%d\n,rid); }由于我的Ubuntu系统当前的环境变量没有主机名这个变量因此主机名结果显示了None。 但是当前的shell只能执行一次程序替换所以需要加上死循环让shell一直运行。 int main() {while(1){char commandline[SIZE];//1.打印命令行信息Interactive(commandline,SIZE);//2.切割字符串Split(commandline);//3.执行命令Execute();}return 0; }2.内建指令 但是有些指令的执行结果是不符合预期的比如cd指令。这部分指令称为内建指令具体请看这篇文章。https://blog.csdn.net/chen1415886044/article/details/103015950 基于这一点我们模拟的shell程序在执行cd指令的时候其实是子程序替换为cd 指令子程序执行了cd指令路径发生改变的仅仅是子程序的路径而我们平时在命令行所打印出来的路径其实都是shell程序的路径运行结果当然不符合预期。 因此在子程序替换执行指令之前先判断要执行的指令是否要内建指令如果是内建指令则不需要创建子进程做替换而是shell这个进程直接执行。 int main() {while(1){char commandline[SIZE];//1.打印命令行信息Interactive(commandline,SIZE);//2.切割字符串Split(commandline);//3.执行内建指令int i BuildinCmd();if(i) continue;//4.执行命令Execute();}return 0; }/执行内建指令 int BuildinCmd() {//判断是否为内建指令如果是则返回1,否则返回0//并且执行内建指令//此处只列举cd这一条内建指令int ret 0;if(strcmp(cd,argv[0]) 0){// cd *** cd到具体路径 // cd 空 cd到家目录char* Target argv[1];if(!Target) Target getenv(HOME);chdir(Target);ret 1;}return ret; }执行结果。 在执行结果中依旧有两个错误。 1.输入为空结果是段错误。 2.cd指令执行后命令行的输出提示中路径并未发生改变。 要解决输入为空后发生的段错误可以在交互的函数中返回输入字符串的长度然后做if条件判断特殊处理。 至于在执行cd指令后显示结果的路径并未发生改变原因就是显示结果是由getenv得到而此时的环境变量PWD并没有发生改变因为当前myshell进程所在路径没有发生改变。 同时不难总结出来cd指令的执行和环境变量PWD的value息息相关。 可以利用snprintf这个函数将格式化信息输出到指定大小的pwd字符串中再利用putenv导入环境变量则myshell程序就模拟出修改环境变量PWD的效果了。 putenv导出环境变量新建或者修改一个环境变量如果putenv的参数是新的环境变量则新建如果是已经存在的环境变量则修改。 snprintf和printf是一类函数printf默认把格式化信息输出到屏幕而多加了s (string)和n(表示字符串的大小的snprintf表示把格式化信息输出到n长度的字符串中。 //关联环境变量,定义一个字符串或者字符数组 char pwd[SIZE]; //执行内建指令 int BuildinCmd() {//判断是否为内建指令如果是则返回1,否则返回0//并且执行内建指令//此处只列举cd这一条内建指令int ret 0;if(strcmp(cd,argv[0]) 0){// cd *** | cd char* Target argv[1];if(!Target) Target getenv(HOME);chdir(Target);//关联环境变量格式化信息会输出到pwd字符数组中snprintf(pwd,SIZE,PWD%s,Target);putenv(pwd);ret 1;}return ret; }但是在用cd指令执行下面这样的情况后路径名并不达预期。 原因是我们是使用Target来更新了环境变量Target是我们输入的内容。 可以利用getcwd函数获取当前进程的绝对路径再用getcwd的返回结果来更新环境变量。 //执行内建指令 int BuildinCmd() {//判断是否为内建指令如果是则返回1,否则返回0//并且执行内建指令//此处只列举cd这一条内建指令int ret 0;if(strcmp(cd,argv[0]) 0){// cd *** | cd char* Target argv[1];if(!Target) Target getenv(HOME);chdir(Target);//关联环境变量格式化信息会输出到pwd字符数组中char tmp[999];getcwd(tmp,999);snprintf(pwd,SIZE,PWD%s,tmp);putenv(pwd);ret 1;}return ret; }export指令也是内建指令在export的指令被切割为argv数组后argv数组的第二个元素就是要导入环境变量的字符串可以直接putenv导入。 if(strcmp(export,argv[0])0) {ret 1;if((argv[1]))putenv(argv[1]); }随便导入一个环境变量执行env命令后就能看到这个环境变量。但是在你执行一系列指令后再执行env指令查看这个环境变量可能会出现找不到的情况。 原因就是上面这段代码是通过argv数组导入的在执行env指令显示的时候指向了argv数组的值而argv数组中的值在一次次执行指令的过程中会不断变换因此已经导入的环境变量可能又会消失不见。 正确做法是用数组保存要导入的环境变量。 //存储新的环境变量 char env[SIZE];if(strcmp(export,argv[0])0) {ret 1;if((argv[1])){strcpy(env,argv[1]);putenv(env);} }echo指令也是内建指令执行echo指令一般有如下几种情况。 echo abcdef //输出一些信息 echo \(HOME //输出环境变量 echo \)? //输出上一次执行结果的进程退出码 echo //输入echo后回车结果会显示回车 需要注意在执行echo \(?这个命令后会显示上一条命令执行的退出码如果再执行一次echo \)?则显示结果应该为0。  int lastExitCode 0; if(strcmp(echo,argv[0])0){ret 1;if(argv[1]){if(argv[1][0] $){if(argv[1][1] ?){printf(%d\n,lastExitCode);lastExitCode0;//重置为0}else{char* tmp2 getenv(argv[1]1);if(tmp2) printf(%s\n,tmp2);else{printf(\n);}}}else{printf(%s\n,argv[1]);}}else{printf(\n);}}小细节为ls指令添加颜色效果。 //切割字符串 void Split(char* in) {int i 0;argv[i] strtok(in,SPC);while(argv[i] strtok(NULL,SPC));if(strcmp(ls,argv[0]) 0){argvi-1–color;argv[i] NULL;} }