网站系统设计目标,系统自动删除了wordpress,wordpress anspress,百度收录网站图片目录
前言#xff1a;
1 命令行解释器部分
2 获取用户命令行参数
3 命令行参数进行分割
4 执行命令
5 判断命令是否为内建命令 前言#xff1a;
本文介绍是自主Shell编写#xff0c;对于shell#xff0c;即外壳解释程序#xff0c;我们目前接触到的命令行解释器
1 命令行解释器部分
2 获取用户命令行参数
3 命令行参数进行分割
4 执行命令
5 判断命令是否为内建命令 前言
本文介绍是自主Shell编写对于shell即外壳解释程序我们目前接触到的命令行解释器有bash还有SSH对于今天模拟实现的Shell编写我们模拟的是bash以及需要的预备知识前文已经介绍了进程的多方面的知识在自主Shell编写里面比较重要的是进程程序替换进程终止进程等待进程状态什么的都是自主Shell编写里面的辅助知识罢了。
那么话不多说我们直接进入到Shell编写部分。 1 命令行解释器部分 我们在Centos版本下进行演示首先我们平常看到的命令行解释器呈现的都是这个模样最开始的_lazy是当前的用户名后面的VM-12-14-centos代表的是当前主机名称后面的~代表的我们所处的当前目录那么我们这里就应该要复刻一个一样的出来。
那么第一个问题来了我们从哪里获取对应的用户名主机名以及目前的目录呢
此时前文引进的环境变量就应该出场了 输入了env之后我们可以在环境变量表里面看到许多对应的环境变量其中HOSTNAMEPWDUSER分别代表的就是主机名称当前路径当前用户名。
那么我们如何通过获取我们已知的是有3种方式一种是environ一种是命令行参数表一种是getenv。
我们这里使用getenv相对于二级指针environgetenv是我们最常见的选择那么我们可以 11 char* argv[] {12 getenv(HOSTNAME),13 getenv(USER),14 getenv(PWD)15 };将获取到的环境变量放在数组argv里面随即进行打印
我们直接使用printf打印数组的三个元素看起来好像没有问题因为命令行参数是在后面输入所以我们不能使用\n作为结束并且这里介绍一个函数snprintf我们不妨使用该函数打印把所有的环境变量放在一个字符串里面似乎更好控制一点这里如果有同学的man手册配置没有齐全的话可以使用指令: sudo yum install man-pages snprintf就是将所有的输出放到一个字符串里面此时我们直接打印该字符即可所以第一部分的临时代码为 34 void OutputBash() 35 { 36 char line[SIZE]; 37 38 char* username GetUser(); 39 char* hostname Gethost(); 40 char* cwd Getcwd();41 42 snprintf(line,sizeof(line),[%s%s %s] ,username,hostname,cwd);43 printf(%s,line);44 fflush(stdout);45 46 // char* argv[] {47 // getenv(HOSTNAME),48 // getenv(USER),49 // getenv(PWD)50 // };51 // char* line;52 // //printf([%s%s %s],argv[0],argv[1],argv[2]);53 // fflush(stdout);54 }8 #define SIZE 5129 10 11 char* GetUser()12 {13 char* user getenv(USER);14 if(user NULL) return NULL;15 return user;16 17 }18 19 char* Gethost() 20 { 21 char* host getenv(HOSTNAME); 22 if(host NULL) return NULL; 23 return host; 24 } 25 26 char* Getcwd() 27 { 28 char* cwd getenv(PWD); 29 if(cwd NULL) return NULL; 30 return cwd; 31 32 } 但是为什么要说这是临时的呢因为我们的pwd并不完善 目前打印的出来并不是最完善的较为完善的应该是只打印当前目录。
那么如何保证修饰一下呢
我们可以将该字符串进行分割也就是使用指针将该指针的指向指到最后一个/指向的地方即可。但是这里不推荐使用函数如果使用的是函数我们就要使用二级指针实属麻烦所以可以使用宏即可 #define SkipPath(p) do{ p (strlen(p)-1); while(*p ! /) p--; }while(0) 那么判断的条件就是只要p碰到了根目录就停下但是有个缺陷就是 /还是存在那么我们可以这样操作 snprintf(line,sizeof(line),[%s%s %s] ,username,hostname,strlen(cwd) 1 ? / : cwd 1); 此时较为完善的命令行解释器部分就打印出来了 2 获取用户命令行参数
第一个问题我们解决了我们现在该获取用户的命令行参数了。
在获取用户命令行参数这里我们要注意的点是我们应该使用什么函数来获取
可不可以使用scanf来获取呢如果使用scanf那么ls -l -n -a能获取到多少呢
我们知道scanf是通过空格或者换行符来获取的此时ls -l -n -a就只能获取到ls所以我们应该换个函数这里推荐fgets其实gets也是可以的但是因为后面有文件的IO操作所以我们使用fgets作为一个缓冲 57 int GetUserCommand(char* usercommand,size_t n) 58 { 59 char* s fgets(usercommand,n,stdin); 60 if(s NULL) return -1; 61 62 return strlen(s); 63 } 但是该代码存在一定的缺陷。
在第4部分会有提示。 3 命令行参数进行分割
获取到了对应的命令那么执行的时候不能带空格去执行吧所以我们要使用函数将命令行参数进行分割这里使用的函数是C语言的库函数strtok相信许多同学已经忘记了不急 第一个参数是分割的字符串第二个参数是分割符那么第一次分割之后将第一个参数置为NULL就会继续分割我们要做的就是将字符串分割之后放到数组里面有益于后面的进程替换工作。
这里定义一个全局变量用于存在分割后的字符串变量
#define SEP char* gArgv[SIZE];这里有一个非常细小的地方如果我们使用单引号的空格虽然也是空格但是和strtok就不匹配了因为这并不是cosnt char* 这只是一个字符而已。 70 void SplitCommand(char* usercommand)71 {72 gArgv[0] strtok(usercommand,SEP);73 int index 1;74 while((gArgv[index] strtok(NULL,SEP)));//分割之后函数返回NULL 恰好作为结尾75 76 }此时有个很不错的代码细节因为函数分割完返回的就是NULL刚好可以作为数组的结束标志。 4 执行命令
到现在我们可以不管三七二十一直接执行命令了至少我们现在先不用管命令是不是内建命令我们就执行几个简单的即可。
那么要执行命令我们肯定涉及到进程程序替换。因为分割好的命令我们已经放在了全局变量里面所以我们可以直接创建函数了 85 void ExcuteCommand()86 {87 pid_t id fork();88 89 if(id 0) Die();90 else if(id 0)91 {92 //child93 execvp(gArgv[0],gArgv);94 exit(1);95 }96 else 97 {98 //father99 int status 0;
100 pid_t rid waitpid(id,status,0);
101 if(rid 0)
102 {
103 lastcode WEXITSTATUS(status);
104 if(lastcode ! 0) printf(%s:%s:%d\n, gArgv[0], strerror(lastcode), lastcode);
105 }
106 }
107
108
109 }这些代码都是进程替换的时候介绍过的了无非是加修饰让代码更加美观此时咱们就可以跑了但是有同学仍会发现不管怎么运行都是不可以的因为我们命令行输入的时候都会自动的输入一个回车这个回车导致了我们跑不了所以我们需要将回车干掉 宏定义ZERO即可。
此时我们就可以正常的执行了。 5 判断命令是否为内建命令
那么现在问题来了如果我们是执行的ehcocd这种内建命令即只能父进程来执行的我们就不能创建子进程了判断是否为内建命令条件成立就内建执行即可并且跳过下一步
那么判断内建命令的方式也是十分简单粗暴的strcmp即可
110 void Cd()
111 {
112 const char *path gArgv[1];
113 if(path NULL) path Gethost();
114 // path 一定存在
115 chdir(path);
116
117 // 刷新环境变量
118 char temp[SIZE*2];
119 getcwd(temp, sizeof(temp));
120 snprintf(cwd, sizeof(cwd), PWD%s, temp);
121 putenv(cwd); // OK
122 }
123
124 int IsInorder()
125 {
126 int yes 0;
127 const char *enter_cmd gArgv[0];
128 if(strcmp(enter_cmd, cd) 0)
129 {
130 yes 1;
131 Cd();
132 }
133 else if(strcmp(enter_cmd, echo) 0 strcmp(gArgv[1], $?) 0)
134 {
135 yes 1;
136 printf(%d\n, lastcode);
137 lastcode 0;
138 }
139 return yes;
140 }这里拿cd举例子判断cd是内建命令之后在cd函数实现因为我们要该目录所以使用函数chdir改变当前工作目录改变了之后改变环境变量中的PATH即可。此时自主shell编写就差不多了。 感谢阅读