目标
- 能处理普通命令
- 能处理内建命令
实现原理
用下面的时间轴来表示时间发生次序。时间从左向右。shell由标识为sh的方块,它随着时间从左向右移动。
shell从用户读入字符串“ls”。shell建立一个新的进程,然后等待进程中运行ls程序并等待进程结束。
然后shell读取新的一行输入,建立一个新的进程在这个进程中运行程序,等待进程结束。
所以写一个shell,需要循环一下过程
- 获取命令行
- 解析命令行
- 建立子进程(fork)
- 替换子进程(execvp)
- 父进程等待子进程退出(wait)
源码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string>
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "
//下面是shell定义的全局数据
// 1.命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0;
// 2.环境变量表
#define MAX_ENV 100
char* g_env[MAX_ENV];
int g_envs = 0;
char cwd[1024];
char cwdenv[1024];
// last code exit
int lastcode = 0;
const char* GetUserName()
{
const char* name=getenv("USER");
return name == NULL ? "None" : name;
}
const char* GetHostName()
{
const char* host=getenv("HOSTNAME");
return host == NULL ? "None" : host;
}
const char* GetPwd()
{
//const char* pwd=getenv("PWD");
const char* pwd = getcwd(cwd,sizeof(cwd));
if(pwd != NULL)
{
snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
putenv(cwdenv);
}
return pwd == NULL ? "None" : pwd;
}
const char* GetHome()
{
const char* home=getenv("HOME");
return home == NULL ? "None" : home;
}
const char* GetOldpwd()
{
const char* oldpwd=getenv("OLDPWD");
return oldpwd == NULL ? "None" : oldpwd;
}
// 处理目录
std::string DirName(const char* pwd)
{
#define SLASH "/"
std::string dir = pwd;
if(dir == SLASH) return SLASH;
auto pos = dir.rfind(SLASH);
if(pos == std::string::npos) return pwd;
return dir.substr(pos+1);
}
void InitEnv()
{
extern char** environ;
memset(g_env,0,sizeof(g_env));
g_envs = 0;
//简化操作,从父进程获得环境变量
for(int i = 0; environ[i]; i++)
{
g_env[i]=(char*)malloc(strlen(environ[i])+1);
strcpy(g_env[i],environ[i]);
g_envs++;
}
g_env[g_envs] = NULL;
//2.导成环境变量
for(int i = 0; g_env[i]; i++)
{
putenv(g_env[i]);
}
environ = g_env;
}
void MakeCommandLine(char cmd_prompt[], int size)
{
snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}
void PrintfCommandPrompt()
{
char command[COMMAND_SIZE];
MakeCommandLine(command,COMMAND_SIZE);
printf("%s",command);
fflush(stdout);
}
// ls -a -l => "ls -a -l"字符串
bool GetCommandLine(char* out, int size)
{
char* c = fgets(out, size, stdin);
if(c==NULL) return false;
out[strlen(out)-1]=0; // 清理\n
if(strlen(out) <= 0) return false;
return true;
}
// 命令行分析 "ls -a -l" => "ls" "-a" "-l"
bool CommandParse(char* commandline)
{
#define SEP " "
g_argc = 0;
g_argv[g_argc++] = strtok(commandline, SEP);
while((bool)(g_argv[g_argc++] = strtok(nullptr,SEP)));
g_argc--;
return g_argc > 0 ? true :false;
}
// command
bool cd()
{
//cd
if(g_argc == 1)
{
std::string home = GetHome();
if(home.empty()) return true;
chdir(home.c_str());
}
else
{
std::string where = g_argv[1];
//cd ~
if(where == "~")
{
std::string home = GetHome();
chdir(home.c_str());
}
//cd -
if(where == "-")
{
std::string oldpwd = GetOldpwd();
chdir(oldpwd.c_str());
}
chdir(where.c_str());
}
return true;
}
void PrintEnv()
{
extern char** environ;
for(int i = 0; environ[i];++i)
{
printf("%-2d->%s\n",i,environ[i]);
}
}
bool CheckAndExcuteBuildin()
{
std::string cmd = g_argv[0];
if(cmd == "cd")
{
cd();
return true;
}
if(cmd == "test")
{
PrintEnv();
return true;
}
return false;
}
void Excute()
{
pid_t id = fork();
if(id == 0) // child
{
execvp(g_argv[0], g_argv);
exit(1);
}
// father
int status = 0;
pid_t rid = waitpid(id,&status,0);
if(rid > 0)
{
lastcode=WEXITSTATUS(status);
}
}
void test_GetCommandLine(char* str)
{
printf("%s\n", str);
}
void test_CommandParse()
{
for(int i = 0; i < g_argc; ++i)
std::cout << g_argv[i] << " " ;
}
void test_InitEnv()
{
for(int i = 0; g_env[i]; i++)
{
printf("env[%2d]->%s\n",i,g_env[i]);
}
printf("envs: %d\n",g_envs);
}
int main()
{
InitEnv();
while(true)
{
// 1. 输出命令行提示符 [jfs@superg-alicloud myshell]$
PrintfCommandPrompt();
// 2. 获取用户输入的命令 ls -a -l
char commandline[COMMAND_SIZE];
if(!GetCommandLine(commandline, sizeof(commandline)))
continue;
//test_GetCommandLine(commandline);
// 3. 命令行分析,"ls -a -l" => "ls" "-a" "-l"
if(!CommandParse(commandline))
continue;
//test_CommandParse();
// 4.检测并处理内建命令
if(CheckAndExcuteBuildin())
continue;
// 5. 执行命令
Excute();
}
return 0;
}