前言
打怪升级:第60天 |
---|
bash模拟
初步代码
#include<stdio.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#define MARK " "
#define MAXSIZE 1024
#define SIZE 50
void Split(char*str, char* command[]) // 对指令进行切分 -- 函数 strtok(目标字符串, 切分条件字符串)
{
int i=0;
command[i] = strtok(str, MARK);// 指令名
//while(command[i++])
//{
// command[i] = strtok(NULL, MARK);
//}
while((command[++i] = strtok(NULL, MARK))); // 化简一步
}
void Show(char* s[]) // 对切分结果打印验证
{
for(int i=0; s[i]; ++i)
printf("%s\n", s[i]);
}
int main()
{
while(1)
{
printf("[陈好人@VM-8-3-centos 落魄山]# "); // 这里为了化简步骤,我们直接打印,不做特殊处理 -- 所以之后使用cd命令后提示语句不会改变
fflush(stdout);
char str[MAXSIZE] = "";
fgets(str, MAXSIZE, stdin);// 获取指令集 -- 此处会将换行符一并读取
str[strlen(str)-1] = '\0'; // 去掉 \n
char* command[SIZE] = {0}; // 对 str进行拆分
Split(str, command);
//varShow(command);
pid_t id = fork();
assert(id >= 0);
if(id == 0) // 使用子进程进行进程替换来调用对应指令
{
execvp(*command, command);
}
else
{
waitpid(id, NULL, 0); // 防止父子交替执行时父进程执行太快。
}
}
return 0;
}
简单优化
#include<stdio.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#define MARK " "
#define MAXSIZE 1024
#define SIZE 50
void Split(char*str, char* command[])
{
int i=0;
command[i] = strtok(str, MARK);// 指令名
//while(command[i++])
//{
// command[i] = strtok(NULL, MARK);
//}
while((command[++i] = strtok(NULL, MARK)));
}
void Show(char* s[])
{
for(int i=0; s[i]; ++i)
printf("%s\n", s[i]);
}
extern char** environ;
extern int putenv(char*);
void PrintEnv()
{
int i = 0;
while(environ[i])
printf("%d : %s\n", i, environ[i++]);
}
int main()
{
while(1)
{
printf("[陈好人@VM-8-3-centos 落魄山]# ");
fflush(stdout);
char str[MAXSIZE] = "";
fgets(str, MAXSIZE, stdin);// 获取指令集 -- 此处会将换行符一并读取
str[strlen(str)-1] = '\0'; // 去掉 \n
char* command[SIZE] = {0}; // 对 str进行拆分
Split(str, command);
//Show(command);
if(strcmp(command[0], "cd") == 0)
{
// cd指令需要父进程自己执行, 调用函数 chdir(char* path);
if(command[1] != NULL) chdir(command[1]);
continue; // 不需要往后执行了,直接返回
}
else if(strcmp(command[0], "env") == 0)
{
PrintEnv();
continue;
}
/* else if(strcmp(command[0], "export") == 0)
{
if(command[1] != NULL)
{
int index = 0;
while(environ[index]) ++index; // 这样也加入不进去
environ[index++] = (char* const)command[1];
environ[index] = NULL;
}
// putenv(command[1]); // 函数使用不了
continue;
}*/
else if(strcmp(command[0], "echo") == 0)
{
if(command[1][0] == '$') printf("%s\n", getenv(command[1] + 1));
else printf("%s\n", command[1]);
continue;
}
if(strcmp(command[0], "ls") == 0)
{
int i = 1;
while(command[i]) ++i;
command[i++] = (char*)"--color=auto"; // 颜色指令追加到最后 -- 注意,不能改变原字符串
command[i] = NULL; // 最后仍然是 NULL
}
pid_t id = fork();
assert(id >= 0);
// (id); // 假装使用一次id,否则编译器会有警告
if(id == 0) // 使用子进程进行进程替换来调用对应指令
{
execvp(*command, command);
}
else
{
waitpid(id, NULL, 0);
}
}
return 0;
}
总结
以上只是我们对bash的十分十分简单的模拟,我们只有这区区一百行,而真正的bash解释器的代码量是以万为单位的,所以我们不需要特别纠结于哪一个哪一个功能没有实现,我们需要做的是理解所用到的语法与函数即可,希望对感兴趣的朋友提供了帮助。