用户和操作系统如何沟通交流,操作系统是不信任用户的,所以提供了一系列的系统接口让用户去调用。shell也是一个应用程序,它提供了界面,并且用户可以输入指令就能够和操作系统打交道。本文,实现一个简单的shell,当然我们不实现提供界面呀的一些复杂操作。我们只要求,输入指令,然后执行指令这样的功能。
(1) 我们知道shell执行完一个指令后,会立马刷新出,然后还能输入指令,所以我们先实现一个不断循环输出的my_shell。
#include
int main()
{
while(1)
{
// 打印提示符
printf("[who@myhostname mydir]#");
fflush(stdout);
}
}
(2) 获取指令
指令是一个字符串,我们默认其长度为128,这个可以利用宏定义,也方便以后修改。利用fgets(),可以获取指令,注意我们获取的指令中含有’\n’,所以我们要去掉末尾的’\n’。
#include
#include
#include
#include
#define NUM 128
int main()
{
char command[NUM];
while(1)
{
//清理s中的数据
command[0]=0;
//打印提示框
printf("[who@myhostname mydir]#");
fflush(stdout);
//获取指令字符串
fgets(command,NUM,stdin);
command[strlen(command)-1]='\0';
}
}
(3) 解析指令
我们获取到了指令,还得解析指令,我们必须拿到这个字符串数组中的每一个字符串。
比如: ls -a -l,存放到了字符串数组中,我们需要把每个字符串,都单拎出来。变成:‘ls’,‘-a’,‘-l’。
所以会用到strtok()这个字符串分解函数,命令行上字符串是以’ '分开的。分解命令字符串后,保存到一个新的指针数组中。指针数组的大小可以定义宏为64。
#include
#include
#include
#include
#define NUM 128
#define comd_NUM 64
int main()
{
char command[NUM];
while(1)
{
char * arv[comd_NUM]={NULL};
s[0]=0;
//
printf("[who@myhostname mydir]#");
fflush(stdout);
//
fgets(command,NUM,stdin);
command[strlen(command)-1]='\0';
//分解字符串,保存到arv中
const char* tal=" ";
arv[0]=strtok(command,tal);
int i=1;
while(arv[i]=strtok(NULL,tal))
{
i++;
}
}
}
(4) 执行指令
指令我们已经取出来了,接下来就应该是执行命令,我们创建一个子进程去执行指令,父进程等待子进程,拿出退出信息。这里用到了进程替换以及进程等待,不懂得小伙伴,可以看我之前写的博客。
#include
#include
#include
#include
#include
#define NUM 128
#define CMD_NUM 64
int main()
{
char command[NUM];
while(1)
{
char *argv[CMD_NUM] = { NULL };
//1. 打印提示符
command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
printf("[who@myhostname mydir]# ");
fflush(stdout);
//2. 获取命令字符串
fgets(command, NUM, stdin);
command[strlen(command) - 1] = '\0'; //"ls\n\0"
//printf("echo: %s\n", command);
//"ls -a -b -c\0";
//3. 解析命令字符串, char *argv[];
//strtok();
const char *sep = " ";
argv[0] = strtok(command, sep);
int i = 1;
while(argv[i] = strtok(NULL, sep)){
i++;
}
}
//4. 执行第三方命令
if(fork() == 0){
//child
execvp(argv[0], argv);
exit(1);
}
int status = 0;
waitpid(-1, &status, 0);
printf("exit code: %d\n", (status >> 8)&0xFF);
}
}
现在我们来看看运行效果:
但是还有一个问题没有解决:
我们来运行一些 cd指令。
可以看到,路径并没有回退,这是什么原因?我们得指令一直用的是子进程来执行,回退路径应该是父进程去执行,需要父进程执行的命令为内键命令,比如:cd。
所以我们需要在加一个判断,是否为”cd“指令。
#include
#include
#include
#include
#include
#define NUM 128
#define CMD_NUM 64
int main()
{
char command[NUM];
for( ; ; ){
char *argv[CMD_NUM] = { NULL };
//1. 打印提示符
command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
printf("[who@myhostname mydir]# ");
fflush(stdout);
//2. 获取命令字符串
fgets(command, NUM, stdin);
command[strlen(command) - 1] = '\0'; //"ls\n\0"
//printf("echo: %s\n", command);
//"ls -a -b -c\0";
//3. 解析命令字符串, char *argv[];
//strtok();
const char *sep = " ";
argv[0] = strtok(command, sep);
int i = 1;
while(argv[i] = strtok(NULL, sep)){
i++;
}
//4.检测命令是否是需要shell本身执行的,内建命令
if(strcmp(argv[0], "cd") == 0){
if(argv[1] != NULL)
chdir(argv[1]);
continue;
}
//5. 执行第三方命令
if(fork() == 0){
//child
execvp(argv[0], argv);
exit(1);
}
int status = 0;
waitpid(-1, &status, 0);
printf("exit code: %d\n", (status >> 8)&0xFF);
}
}
chdir(),是更改路径的函数。
以上就是我们shell的模拟实现