0


【Linux】Shell命令行的简易实现(C语言实现)内键命令,普通命令

文章目录


0.准备工作

在这里插入图片描述

1.大体框架

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<assert.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/wait.h>#defineLEFT"["#defineRIGHT"]"#defineLABLE"#"#defineDELIM" \t"//用于修饰命令行//类似:[hh@VM-4-10-centos ~]$ #defineLINE_SIZE1024//输入命令最大长度#defineARGC_SIZE32//命令行参数表的大小#defineEXIT_CODE44//退出码int lastcode =0;//上一次的退出码int quit =0char commandline[LINE_SIZE];//输入的命令char*argv[ARGC_SIZE];//解析后保存的命令char pwd[LINE_SIZE];//保存当前所在路径// 自定义环境变量表char myenv[LINE_SIZE];//因为环境变量表里面保存的不是//变量本身,而是其地址,所以我们为了防止//自己导入的环境变量被覆盖,需要自己维护一段空间//这里myenv只能维护一个环境变量,因为只有一个地址constchar*getusername(){//获取用户名returngetenv("USER");}constchar*gethostname(){//获取主机名returngetenv("HOSTNAME");}voidgetpwd(){//将当前路径保存到pwd中getcwd(pwd,sizeof(pwd));}voidinteract(char*cline,int size){}//获取命令行intsplitstring(char cline[],char*_argv[]){}//解析命令行voidNormalExcute(char*_argv[]){}//执行普通命令intbuildCommand(char*_argv[],int _argc){}//执行内键命令intmain(){while(!quit){// 1.// 2. 交互问题,获取命令行 interact(commandline,sizeof(commandline));// 3. 子串分割的问题,解析命令行int argc =splitstring(commandline, argv);if(argc ==0)continue;// 4. 指令的判断 //内键命令,本质就是一个shell内部的一个函数int n =buildCommand(argv, argc);// 5. 普通命令的执行if(!n)NormalExcute(argv);}return0;}

一、获取命令行

在获取命令之前我们需要先建立一个命令行
类似这种效果:在这里插入图片描述

voidinteract(char*cline,int size){getpwd();//将当前路径写入到pwd中printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),gethostname(), pwd);fgets(cline, size,stdin);//这里不用scanf的原因是其遇到空格与回车不会读取//所以我们选择用fgets,将命令写入cline中// "abcd\n\0"//又因为fgets会读入回车键,所以我们要手动把回车位置改为'\0'
    cline[strlen(cline)-1]='\0';}

二、解析命令行

intsplitstring(char cline[],char*_argv[]){int i =0;//strtok用于分割字符串,上面我们宏定义了只有要DELIM中的字符//就会发生分割,将分割后的字符串写入argv中
    argv[i++]=strtok(cline, DELIM);while(_argv[i++]=strtok(NULL, DELIM));// 故意写的=return i -1;//返回argv中存的字符串个数}

三、进程执行

1.普通命令

voidNormalExcute(char*_argv[]){pid_t id =fork();//创建子进程if(id <0){perror("fork");return;}elseif(id ==0){//让子进程执行命令//execvp相当于一个加载器//该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
       例程开始执行
       //会从环境变量中的路径中找到我们的可执行程序execvp(_argv[0], _argv);exit(EXIT_CODE);}else{int status =0;pid_t rid =waitpid(id,&status,0);if(rid == id){//等待子进程成功,更改退出码
            lastcode =WEXITSTATUS(status);}}}

2.内建命令

在这里插入图片描述

intbuildCommand(char*_argv[],int _argc){if(_argc ==2&&strcmp(_argv[0],"cd")==0){//chdir改变当前进程路径//假如我们让子进程执行cd命令,子进程确实路径改变了//但子进程执行完就被父进程回收了,没屁用//因为进程的独立性我父进程路径不受影响。//所以我们要手动修改路径chdir(argv[1]);getpwd();//将新的路径写入pwd//改变环境变量PWD,用pwd对其进行写入sprintf(getenv("PWD"),"%s", pwd);return1;}elseif(_argc ==2&&strcmp(_argv[0],"export")==0){//自己维护环境变量空间strcpy(myenv, _argv[1]);//将环境变量放入自己的myenvputenv(myenv);return1;}elseif(_argc ==2&&strcmp(_argv[0],"echo")==0){if(strcmp(_argv[1],"$?")==0){printf("%d\n", lastcode);
            lastcode=0;}elseif(*_argv[1]=='$'){//_argv[1]+1为$后面的值例如$PATH//那么最后就获取PATH的值char*val =getenv(_argv[1]+1);if(val)printf("%s\n", val);}else{printf("%s\n", _argv[1]);}return1;}// 特殊处理一下ls//因为ls中如果是可执行文件,其会显示为特殊颜色if(strcmp(_argv[0],"ls")==0){
        _argv[_argc++]="--color";
        _argv[_argc]=NULL;}return0;}

四、完整代码:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<assert.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/wait.h>#defineLEFT"["#defineRIGHT"]"#defineLABLE"#"#defineDELIM" \t"#defineLINE_SIZE1024#defineARGC_SIZE32#defineEXIT_CODE44int lastcode =0;int quit =0;externchar**environ;char commandline[LINE_SIZE];char*argv[ARGC_SIZE];char pwd[LINE_SIZE];// 自定义环境变量表char myenv[LINE_SIZE];constchar*getusername(){returngetenv("USER");}constchar*gethostname(){returngetenv("HOSTNAME");}voidgetpwd(){getcwd(pwd,sizeof(pwd));}voidinteract(char*cline,int size){getpwd();printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),gethostname(), pwd);fgets(cline, size,stdin);// "abcd\n\0"
    cline[strlen(cline)-1]='\0';}intsplitstring(char cline[],char*_argv[]){int i =0;
    argv[i++]=strtok(cline, DELIM);while(_argv[i++]=strtok(NULL, DELIM));return i -1;}voidNormalExcute(char*_argv[]){
    pid_t id =fork();if(id <0){perror("fork");return;}elseif(id ==0){//让子进程执行命令//execvpe(_argv[0], _argv, environ);execvp(_argv[0], _argv);exit(EXIT_CODE);}else{int status =0;
        pid_t rid =waitpid(id,&status,0);if(rid == id){
            lastcode =WEXITSTATUS(status);}}}intbuildCommand(char*_argv[],int _argc){if(_argc ==2&&strcmp(_argv[0],"cd")==0){chdir(argv[1]);getpwd();sprintf(getenv("PWD"),"%s", pwd);return1;}elseif(_argc ==2&&strcmp(_argv[0],"export")==0){strcpy(myenv, _argv[1]);putenv(myenv);return1;}elseif(_argc ==2&&strcmp(_argv[0],"echo")==0){if(strcmp(_argv[1],"$?")==0){printf("%d\n", lastcode);
            lastcode=0;}elseif(*_argv[1]=='$'){char*val =getenv(_argv[1]+1);if(val)printf("%s\n", val);}else{printf("%s\n", _argv[1]);}return1;}// 特殊处理一下lsif(strcmp(_argv[0],"ls")==0){
        _argv[_argc++]="--color";
        _argv[_argc]=NULL;}return0;}intmain(){while(!quit){// 1.// 2. 交互问题,获取命令行  interact(commandline,sizeof(commandline));// 3. 子串分割的问题,解析命令行int argc =splitstring(commandline, argv);if(argc ==0)continue;// 4. 指令的判断 //内键命令,本质就是一个shell内部的一个函数int n =buildCommand(argv, argc);// 5. 普通命令的执行if(!n)NormalExcute(argv);}return0;}
标签: linux c语言 运维

本文转载自: https://blog.csdn.net/m0_74774759/article/details/134255146
版权归原作者 Satoru_Kaugo 所有, 如有侵权,请联系我们删除。

“【Linux】Shell命令行的简易实现(C语言实现)内键命令,普通命令”的评论:

还没有评论