前言
本小节是完善我们上次完成[Linux]下制作简易myshell这篇博客。
上次因为我们的知识还不是很完善,所以留了一些小尾巴,本篇博客来将我们的myshell完成。
一、给ls命令加颜色
相比于系统调用接口,我们少了颜色标识。
那如何让我们自己制作的myshell也添加上颜色标识呢?
which ls
alias这个单词是取别名的意思。
举个栗子!
这个取别名操作在我们重启Xshell,就没有了。想要永久保存,需要我们修改配置文件。
所以在底层我们调用’ls’命令,其实是调用了’ls --color=auto’
//给ls命令添加颜色
if(strcmp(command_args[0],“ls”)==0)
command_args[index++]=(char*)“–color=auto”;
int main()
{
//shell本质上就是一个死循环
while(1)
{
//不关心获取这些属性的接口,搜索一下
//1.显示提示符
printf("[曾曾@我的主机名 当前目录]# ");
fflush(stdout);
//2.获取用户输入
memset(command_line,'\0',sizeof(command_line)*sizeof(char));
fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
command_line[strlen(command_line)-1]='\0';
//3.“ls -a -l”--->"ls" "-a" "-l" 截取字符串
command_args[0]=strtok(command_line,SEP);
int index=1;
//给ls命令添加颜色
if(strcmp(command_args[0],"ls")==0)
command_args[index++]=(char*)"--color=auto";
while(command_args[index++]=strtok(NULL,SEP));
//5.创建进程,执行
pid_t id=fork();
if(id==0)
{
//child
//6.程序替换
execvp(command_args[0],command_args);
exit(1);//执行到这里,子进程一定替换失败了
}
int status=0;
pid_t ret=waitpid(id,&status,0);
if(ret>0)
{
printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);
}//end while
}
return 0;
}
现在我们的’ls’命令也带上了颜色标识!!!
二、内建命令
2.1 cd命令
我们在进行’cd …'回退到上级目录的时候,我发现我当前所处的路径没有发生任何变化!
我们如果直接exec*执行cd,最多只是让子进程进行路径切换,子进程是一运行就完毕的进程的进程!我们在shell中,更希望谁的路径发生变化呢?
答:父进程,也就是shell本身!!!
如果有些行为,是必须让父进程shell执行的,不想让子进程执行,所以我们绝对不能创建子进程!只能是父进程自己实现对应的代码!
由shell自己执行的命令,我们称之为内建(内置 bind-in)命令!–>相当于shell内部的一个函数!
//对应上层的内建命令
int ChangeDir(const char* new_path)
{
chdir(new_path);
return 0;//调用成功
}
int main()
{
//shell本质上就是一个死循环
while(1)
{
//不关心获取这些属性的接口,搜索一下
//1.显示提示符
printf("[曾曾@我的主机名 当前目录]# ");
fflush(stdout);
//2.获取用户输入
memset(command_line,'\0',sizeof(command_line)*sizeof(char));
fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
command_line[strlen(command_line)-1]='\0';
//3.“ls -a -l”--->"ls" "-a" "-l" 截取字符串
command_args[0]=strtok(command_line,SEP);
int index=1;
//给ls命令添加颜色
if(strcmp(command_args[0],"ls")==0)
command_args[index++]=(char*)"--color=auto";
while(command_args[index++]=strtok(NULL,SEP));
//4.TODO 编写后面的逻辑,内建命令
if(strcmp(command_args[0],"cd")==0&&command_args[1]!=NULL)
{
ChangeDir(command_args[1]);//让调用方进行路径切换,父进程
continue;//结束本次循环!!!
}
//5.创建进程,执行
pid_t id=fork();
if(id==0)
{
//child
//6.程序替换
execvp(command_args[0],command_args);
exit(1);//执行到这里,子进程一定替换失败了
}
int status=0;
pid_t ret=waitpid(id,&status,0);
if(ret>0)
{
printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);
}//end while
}
return 0;
}
这样我们就完成了路径的切换!!!
2.2 环境变量
首先来获取环境变量!(这个在我们之前的博客讲过,忘记了可以回顾以前的内容)。
#include<stdio.h>
int main()
{
extern char** environ;
for(int i=0;environ[i]!=NULL;i++)
{
printf("[%d]:%s\n",i,environ[i]);
}
return 0;
}
现在我想导入一个环境变量!
export 变量名
我们使用我们自己制作的myshell来运行一下该进程。
我们也看到了相同的环境变量。我们发现默认会将父进程的环境变量继承到子进程的!
接下来我们想要在子进程导入环境变量,该怎么做呢?
我们导入一个环境变量’MYVAL’
导入之前系统是没有的!
导入后!
这个导入环境变量的操作也需要导入给我们的父进程,当然也就需要内建命令!!
程序env
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("MYVAL=%s\n",getenv("MYVAL"));
return 0;
}
myshell
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<sys/types.h>
#define SEP " "
#define NUM 1024
#define SIZE 128
char command_line[NUM];
char* command_args[SIZE];
char env_buffer[NUM];//for test
//对应上层的内建命令
int ChangeDir(const char* new_path)
{
chdir(new_path);
return 0;//调用成功
}
void PutEnvInMyShell(char* new_env)
{
putenv(new_env);
}
int main()
{
//shell本质上就是一个死循环
while(1)
{
//不关心获取这些属性的接口,搜索一下
//1.显示提示符
printf("[曾曾@我的主机名 当前目录]# ");
fflush(stdout);
//2.获取用户输入
memset(command_line,'\0',sizeof(command_line)*sizeof(char));
fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
command_line[strlen(command_line)-1]='\0';
//3.“ls -a -l”--->"ls" "-a" "-l" 截取字符串
command_args[0]=strtok(command_line,SEP);
int index=1;
//给ls命令添加颜色
if(strcmp(command_args[0],"ls")==0)
command_args[index++]=(char*)"--color=auto";
while(command_args[index++]=strtok(NULL,SEP));
//4.TODO 编写后面的逻辑,内建命令
if(strcmp(command_args[0],"cd")==0&&command_args[1]!=NULL)
{
ChangeDir(command_args[1]);//让调用方进行路径切换,父进程
continue;
}
if(strcmp(command_args[0],"export")==0&&command_args[1]!=NULL)
{
//目前,环境变量的信息在command_line,会被清空
//此处我们需要自己保存一下环境变量内容
strcpy(env_buffer,command_args[1]);
PutEnvInMyShell(command_args[1]);
continue;
}
//5.创建进程,执行
pid_t id=fork();
if(id==0)
{
//child
//6.程序替换
execvp(command_args[0],command_args);
exit(1);//执行到这里,子进程一定替换失败了
}
int status=0;
pid_t ret=waitpid(id,&status,0);
if(ret>0)
{
printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);
}//end while
}
return 0;
}
此时就将环境变量导入成功了!
三、重定向操作
此时我们发现自己完成的shell并不支持重定向操作!
首先我们需要先对输入的命令行内容进行分解!
分别找到是重定向还是追加重定向还是输出重定向!
然后再进行分解得到文件名和标记位。进行封装后利用我们上节博客学到的dup2进行重定向操作后就可以完成我们的需求!
获取文件名和标记位
进行重定向操作后!
至此我们就将myshell的操作完美进行了模拟实现。和Xshell的功能大差不差!!!
(本章完!)