shell的解释器本质就是一段代码,用来解释不同的指令,然后做做出相同的操作,下面我们自己编写一个简易的shell
#include<stdio.h>
#include<unistd.h>
#include<cstdlib>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>
#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
//宏函数
#define SkipPath(cwd) do{ cwd+=(strlen(cwd)-1);while(*cwd!='/')cwd-- ;}while(0)
char cwd[SIZE*2];
char* gArgv[NUM];//命令行数组
int lastcode=0;//退出码
void Die()
{
exit(1);
}
//获取家目录
const char* GetHome()
{
const char*home=getenv("HOME");
if(home==NULL)return "/";
return home;
}
const char* GetUserName()
{
const char* name=getenv("USER");//从环境变量中获取USER路径
if(name==NULL)return "None";
return name;
}
const char* GetHostName()
{
const char* name=getenv("HOSTNAME");//从环境变量中获取HOSTNAME路径
if(name==NULL)return "None";
return name;
}
const char* GetCwd()
{
const char* cwd=getenv("PWD");//从环境变量中获取PWD路径
if(cwd==NULL)return "None";
return cwd;
}
void MakeCommandLineAndPrint()
{
char line[SIZE];
const char* username=GetUserName();
const char* hostname=GetHostName();
const char* cwd=GetCwd();
SkipPath(cwd);//分割路径,只需要最后面的一个路径
//将用户名,主机名,路径都写入line中,cwd+1的原因是不要命令前面的/了,
//比如/ls ,直接就是ls
snprintf(line,sizeof(line),"[%s@%s %s]> ",username,hostname,cwd+1);
printf("%s",line);
fflush(stdout);//刷新缓冲区
}
int GetUserCommand(char command[],size_t n)
{
char* s=fgets(command,n,stdin);//输入命令
if(s==NULL)return -1;
command[strlen(command)-1]=ZERO;//在命令行的结尾改成\0,因为我们输完命令行,
//要点回车,\0在回车前面,就可以把回车忽略掉了
return strlen(command);
}
void SplitCommand(char command[],size_t n)
{
(void)n;
//"ls -a -l -n"-> "ls" "-a" "-l" "-n"
//分割命令行字符串
gArgv[0]=strtok(command,SEP);
int index=1;
while((gArgv[index++]=strtok(NULL,SEP)));
}
//比较难理解
void Cd()
{
const char*path=gArgv[1];
if(path==NULL)path=GetHome();//如果cd后面什么也不跟,直接返回家目录
chdir(path); //改变当前工作路径
char temp[SIZE*2];
getcwd(temp,sizeof(temp));//获取当前工作路径
snprintf(cwd,sizeof(cwd),"PWD=%s",temp);
putenv(cwd);
}
int CheckBuildin()
{
int yes=0;
const char* enter_cmd=gArgv[0];
if(strcmp(enter_cmd,"cd")==0)
{
yes=1;
Cd();
}
else if(strcmp(enter_cmd,"echo")==0&&strcmp(gArgv[1],"$?")==0)
{
yes=1;
printf("%d\n",lastcode);
lastcode=0;
}
return yes;
}
void ExecuteCommand()
{
pid_t id=fork();
if(id<0)Die();
else if(id==0)
{
//child
execvp(gArgv[0],gArgv);
exit(errno);
}
else
{
//father
int status=0;
pid_t rid=waitpid(id,&status,0);
if(rid>0)
{
lastcode=WEXITSTATUS(status);
if(lastcode!=0)
printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
}
}
}
int main()
{
int quit=0;
while(!quit)
{
//1.输出命令行
MakeCommandLineAndPrint();
//2.获取用户命令字符串
char usercommand[SIZE];
int n=GetUserCommand(usercommand,sizeof(usercommand));
if(n<=0)return 1;
//3.命令行字符串分割
SplitCommand(usercommand,sizeof(usercommand));
//4.检测命令是否是内建命令
n=CheckBuildin();
if(n) continue;
//5.执行命令
ExecuteCommand();
}
return 0;
}