vpsputty做网站,免费域名注册 国外,关键词排名零芯互联关键词,土木工程招聘网最新招聘信息文章目录 函数和进程之间的相似性shell打印提示符#xff0c;以及获取用户输入分割用户的输入判断是否是内建命令执行相关的命令 全部代码 正文开始前给大家推荐个网站#xff0c;前些天发现了一个巨牛的
人工智能学习网站#xff0c;
通俗易懂#xff0c;风趣幽默#… 文章目录 函数和进程之间的相似性shell打印提示符以及获取用户输入分割用户的输入判断是否是内建命令执行相关的命令 全部代码 正文开始前给大家推荐个网站前些天发现了一个巨牛的
人工智能学习网站
通俗易懂风趣幽默忍不住分享一下给大家。[点击跳转到网站] 函数和进程之间的相似性
exec/exit就像call/return一个C程序有很多函数组成。一个函数可以调用另外一个函数同时传递给它一些参数。被调用的函数执行一定的操作然后返回一个值。每个函数都有他的局部变量不同的函数通过call/return系统进行通信。这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化程序设计的基础。Linux鼓励将这种应用于程序之内的模式扩展到程序之间 一个C程序可以fork/exec另一个程序并传给它一些参数。这个被调用的程序执行一定的操作然后通过exit(n)来返回值。调用它的进程可以通过waitret来获取exit的返回值。
shell
有了程序替换我们可以让一个进程来调用其他进程所以我们想如果我们在执行程序替换的时候创建一个子进程让子进程去执行然后我们主进程(父进程)来和用户交互我们是不是就可以实现一个简易的shell命令行解释器。
打印提示符以及获取用户输入 我们可以看到每次在运行的时候shell都会先打印前面的一堆东西并且光标会停留在后面等待用户输入所以我们父进程可以先把打印以及输入这个工作搞定。 那么这一堆东西如何获取打印呢我们可以直接写死但是换个主机可能就不满足要求了我们可以分析一下前面的这一堆东西是不是可以通过环境变量来获取呢我们可以查一下环境变量 我们可以发现前面是用户主机名 然后加当前路径所以我们可以通过环境变量来动态获取这些内容。 我们可以先来三个函数一个用来获取用户名一个用来获取当前路径一个用来获取主机名。
char *getUsername()
{char *name getenv(USER);if (name)return name;return none;
}char *getHostname()
{char *name getenv(HOSTNAME);if (name)return name;return none;
}char *getCwd()
{char *cwd getenv(PWD);if (cwd)return cwd;return none;
}然后我们可以定义一个缓冲区用来保存用户的输入然后把打印起那么的一堆东西和输入放到一个函数里进行一下封装。
int getComment(char comment[], int num)
{printf([%s%s %s]# , getUsername(), getHostname(), getCwd());char *ch fgets(comment, num, stdin);if (ch NULL)return -1;int n strlen(comment);comment[n - 1] \0;return n - 1;
}这里面的细节还是挺多的不管怎么说用户一定会输入一个回车所以我们不用担心 comment[n - 1] ‘\0’ 会越界访问对于返回值问题如果用户输入失败或者没有输入了内容返回值就会是一个 0的数就没必要进行后面创建子进程等的工作直接跳过此次循环就OK。所以我们只要传进来一个缓冲区就可以了。这个函数就会把用户输入的内容放到缓冲区中去。
分割用户的输入
用户输入进来的诸如ls -a -l等等都是一整个字符串但是我们进程程序替换是需要将这些个子串以空格分割进行分割成若干个子串所以我们可以把这部分在进行封装成一个函数我们把用户输入的内容传给这个函数让这个函数把用户输入的内容分割成若干个子串然后传出去就可以了分割子串我们可以用到C语言的strtok函数还是比较简单的。
#define SEP
void commentSplit(char *in, char *out[])
{int pos 0;out[pos] strtok(in, SEP);while (out[pos] strtok(NULL, SEP));
}判断是否是内建命令
我们学到的命令由两部分有一部分是普通命令也就是shell创建子进程让子进程去执行的还有一部分就是内建命令如cd,export,echo等这些命令是shell的一个函数是需要shell自己去执行的。所以我们在创建子进程之前需要判断一下是否是内建命令如果是的话就没必要创建子进程了所以我们可以设计一个返回值来进行区分是否是内建命令。
char cwd[NUM];
char add_env[NUM][NUM];
int envi 0;
static int lastcode 0;char* getHomePath()
{char* path getenv(HOME);if(path) return path;return none;
}
void cd(const char* path)
{char tmp[NUM];//改变工作目录为当前的pathchdir(path);//获取当前的工作目录getcwd(tmp,sizeof tmp);sprintf(cwd,PWD%s,tmp);putenv(cwd);
}
int dobuildcom(char* argv[])
{if(strcmp(argv[0],cd) 0){char* path;if(argv[1] NULL) path getHomePath();else if(strcmp(argv[1], ~) 0) {path getHomePath();}else path argv[1];cd(path);return 1;}else if(strcmp(argv[0],export) 0){if(argv[1] NULL) return 1;strcpy(add_env[envi],argv[1]);putenv(add_env[envi]);envi;return 1;}else if(strcmp(argv[0],echo) 0){if(argv[0] NULL) {printf(\n);return 1;}char* tmp argv[1];if(tmp[0] $){tmp;if(strcmp(tmp,?) 0) {printf(%d\n,lastcode);}else{char* c getenv(tmp);if(c) printf(%s\n,c);else printf(\n);}}else{printf(%s\n,tmp);}lastcode 0;return 1;}return 0;
}我们这里就实现了3个内建命令这里面最值的一说的就是环境变量了所以我们在cd或者使用export添加环境时是不能使用临时变量的因为环境变量拥有全局属性如果使用局部变量的话当函数执行完之后这个空间就会还给OS,所以我们在导环境环境变量的时候需要定义全局变量然后把需要导的环境变量拷贝到全局变量中在进行添加环境变量。返回值是1就是内建命令否则就不是。
执行相关的命令
我们知道了程序替换这个就比较简单了我们只需要将分割好的子串给这个函数然后我们有了子串数组我们那选择好程序替换函数就可以了因为我们直接就有子串的数组所以我们可以选择execvp这个函数。
void execute(char *argv[])
{pid_t id fork();if (id 0){execvp(argv[0], argv);exit(0);}int status 0;waitpid(id, status, 0);lastcode WEXITSTATUS(status);
}我们在执行完一个命令后我们有一个全局变量来记录最后一条命令执行的结果所以我们可以获取一下退出码赋值给这个变量。
全部代码
到这里我们的自定义shell的整体逻辑差不多就结束了最后奉上全部代码。
#include stdio.h
#include unistd.h
#include stdlib.h
#include string.h
#include sys/types.h
#include sys/wait.h
#include sys/stat.h
#include fcntl.h#define NUM 1024
#define SEP extern int putenv(char* );char cwd[NUM];
char add_env[NUM][NUM];
int envi 0;
static int lastcode 0;char *getUsername()
{char *name getenv(USER);if (name)return name;return none;
}char *getHostname()
{char *name getenv(HOSTNAME);if (name)return name;return none;
}char *getCwd()
{char *cwd getenv(PWD);if (cwd)return cwd;return none;
}char* getHomePath()
{char* path getenv(HOME);if(path) return path;return none;
}int getComment(char comment[], int num)
{printf([%s%s %s]# , getUsername(), getHostname(), getCwd());char *ch fgets(comment, num, stdin);if (ch NULL)return -1;int n strlen(comment);comment[n - 1] \0;return n - 1;
}void commentSplit(char *in, char *out[])
{int pos 0;out[pos] strtok(in, SEP);while (out[pos] strtok(NULL, SEP));
}void execute(char *argv[])
{pid_t id fork();if (id 0){execvp(argv[0], argv);exit(0);}int status 0;waitpid(id, status, 0);lastcode WEXITSTATUS(status);
}void cd(const char* path)
{char tmp[NUM];chdir(path);getcwd(tmp,sizeof tmp);sprintf(cwd,PWD%s,tmp);putenv(cwd);
}
int dobuildcom(char* argv[])
{if(strcmp(argv[0],cd) 0){char* path;if(argv[1] NULL) path getHomePath();else if(strcmp(argv[1], ~) 0) {path getHomePath();}else path argv[1];cd(path);return 1;}else if(strcmp(argv[0],export) 0){if(argv[1] NULL) return 1;strcpy(add_env[envi],argv[1]);putenv(add_env[envi]);envi;return 1;}else if(strcmp(argv[0],echo) 0){if(argv[0] NULL) {printf(\n);return 1;}char* tmp argv[1];if(tmp[0] $){tmp;if(strcmp(tmp,?) 0) {printf(%d\n,lastcode);}else{char* c getenv(tmp);if(c) printf(%s\n,c);else printf(\n);}}else{printf(%s\n,tmp);}lastcode 0;return 1;}return 0;
}
int main()
{while (1){char usercomment[NUM];char *argv[64] {NULL};int n getComment(usercomment, sizeof usercomment);if(n 0) continue;// 分割字符串commentSplit(usercomment, argv);//检查并内建命令n dobuildcom(argv);if(n) continue;// 执行命令execute(argv);}return 0;
}