目录
前言
之前学习了很多进程相关的知识,包括环境变量、进程的创建与退出、进程等待、进程替换。现在可以用所学的作一个小总结,手撕一个shell解释器,大致的思路是先通过环境变量获取相关信息,再通过fork创建子进程并进行程序替换,bash的命令。
一、打印提示信息
当我们链接上虚拟机,就会有这一行输出到屏幕上,告诉我们可以开始输入命令了。首先我们得把提示写出来。
环境变量中有用户名、主机号、当前目录等信息,我们可以通过 getenv 获取相关信息进行打印。
同时,我们输入命令会有很多空格存在,比如 ls -a -l。因此不能用scanf获取输入信息,可以用fgets,第三个参数为stdin(标准输入)。最后输入完毕后会输入回车换行,我们将最后一个字符设置为 '\0' 代表字符串的结束,同时也避免了换行。
#inlcude<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define NUM 1024
char* getUsername()
{
char* env = getenv("USER");
if(env) return env;
return NULL;
}
char* getHostname()
{
char* env = getenv("HOSTNAME");
if(env) return env;
return NULL;
}
char* getPwd()
{
char* env = getenv("PWD");
if(env) return env;
return NULL;
}
int main()
{
char command[NUM];
printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd());//打印
fgets(command,NUM,stdin); //输入完成后还会输入回车,导致换行
command[strlen(command)-1] = '\0';
printf("%s",command); // 打印看看是否获取到了完整的字符串
}
运行结果如下,获取了完整了字符串。
二、分割字符串
分割字符串C语言可以用strtok函数,他第一个参数为需要分割的字符串,第二个参数是按什么字符进行分割。如果想继续往后分割同一字符传,后续需要将第一个参数设置为NULL。
分割成功返回值为分割出来的字符串,如果分割失败,返回0。
我们先分割一次字符串,放到数组argv里,后面使用while循环一直进行分割,由于分割失败返回0,自然而然就退出了。下面是打印代码,看看结果是否正确。
成功分割。
三、替换程序
fork出子进程,然后使用execvp进行程序替换,第一个参数为argv[0],比如你输入ls -a -l,他会自己去path路径里面查找 ls 是否存在,第二个参数为agrv,整个数组放进去,是命令行参数。这里写简单一点,没有处理等待失败的情况。
成功进行替换。
最后给他套上循环,一个建议的shell就做好了。我们写的比较简单,有很多bug,功能还不算完善,但是勉强也算够用。
附上总代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define NUM 1024
#define SIZE 64
char* getUsername()
{
char* env = getenv("USER");
if(env) return env;
return NULL;
}
char* getHostname()
{
char* env = getenv("HOSTNAME");
if(env) return env;
return NULL;
}
char* getPwd()
{
char* env = getenv("PWD");
if(env) return env;
return NULL;
}
int main()
{
while(1)
{
char command[NUM];
char* argv[SIZE];
int argc = 0;
printf("[%s@%s %s]$ ",getUsername(),getHostname(),getPwd());//打印
fgets(command,NUM,stdin); //输入完成后还会输入回车,导致换行
command[strlen(command)-1] = '\0';
argv[argc++] = strtok(command," ");
while(argv[argc++] = strtok(NULL, " "));
pid_t id = fork();
if(id == 0)
{
//child
execvp(argv[0],argv);
exit(1);
}
else
{
pid_t rid = waitpid(id,NULL,0);
if(rid>0) printf("等待成功\n");
}
}
}
这里完善了一下代码,添加了重定向,在大标题“四”。