文章目录
- 1.简易实现
- 2.人机交互,获取命令行
- 3.命令行分割
- 4.执行命令
- 5.内建命令
- 6.myshell代码
1.简易实现
2.人机交互,获取命令行
代码如下:
int quit=0;
#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define LINE_SIZE 1024
char pwd[LINE_SIZE];
char commandline[LINE_SIZE];
const char* getusername()
{
return getenv("USER");
}
void getpwd()
{
getcwd(pwd, sizeof(pwd));
}
void interact(char* cline, int size)
{
//获取主机名
char hostname[256];
gethostname(hostname, sizeof(hostname));
//获取当前路径
getpwd();
printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(),hostname, pwd);
char* s = fgets(cline, size, stdin);
assert(s);//debug版本下assert才起效果
//release,assert失效
(void)s;//使用一下,可以在不同版本都生效,抵挡编译器报警
//"abcd\n\0"
cline[strlen(cline)-1] = '\0';
}
int main()
{
while(!quit)
{
//交互问题,获取命令行
interact(commandline,sizeof(commandline));
printf("%s\n",commandline );
}
return 0;
}
代码运行的的结果为:
运行结果如上,程序获取命令行以及提示行显示的功能已经完成了。
3.命令行分割
代码如下:
#define DELIM " "
#define ARGC_SIZE 32
char* argv[ARGC_SIZE];
int splitstring(char cline[], char* _argv[])
{
int i = 0;
_argv[i++] = strtok(cline, DELIM);
//stork函数扫描字符串末尾时,返回空指针
while(_argv[i++] = strtok(NULL,DELIM));//故意写的=
return i - 1;
}
int main()
{
while(!quit)
{
//交互问题,获取命令行
interact(commandline,sizeof(commandline));
//3.子串分割问题,解析命令行
int argc = splitstring(commandline,argv);
if(argc == 0) continue;
for(int i = 0; i < argc; i++)
{
printf("%s\t",argv[i]);
}
printf("\n");
}
return 0;
}
代码运行的的结果为:
运行结果如上,命令行分割成字符串并存放进入数组使用。
4.执行命令
代码如下:
#define EXIT_CODE 44
int lastcode = 0;
void NormalExcute(char* _argv[])
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
return;
}
else if(id == 0)
{
//让子进程执行命令
execvp(_argv[0],_argv);
exit(EXIT_CODE);
}
else
{
int status = 0;
pid_t rid = waitpid(id, &status,0);
if(rid == id)
{
lastcode = WEXITSTATUS(status);
}
}
}
int main()
{
while(!quit)
{
//1.交互问题,获取命令行
interact(commandline,sizeof(commandline));
//2.子串分割问题,解析命令行
int argc = splitstring(commandline,argv);
if(argc == 0) continue;
for(int i = 0; i < argc; i++)
{
//printf("%d:",i);
//printf("%s\n",argv[0]);
printf("%s\n",argv[i]);
}
printf("\n");
//3.普通命令的执行
//if(!n)
NormalExcute(argv);
}
return 0;
}
代码运行的结果:
运行结果如上,可以让子进程执行替换函数,调用系统程序命令执行!
5.内建命令
我们发现使用一些命令的时候没有结果,诸如“”cd\echo”
等命令,因为这些命令属于内建命令,是要父进程执行的。
代码如下:
#define EXIT_CODE 44
int lastcode = 0;
//自定义环境变量表
char myenv[LINE_SIZE];
//自定义本地变量表
int buildCommand(char* _argv[], int _argc)
{
if(_argc == 2 && strcmp(_argv[0], "cd") == 0)
{
//修改父进程中的当前路径
chdir(argv[1]);
//把修改路径放到pwd数组中
getpwd();
//把pwd数组的路径,放到环境变量中
sprintf(getenv("PWD"), "%s", pwd);
return 1;
}
else if(_argc == 2 && strcmp(_argv[0], "export") == 0)
{
strcpy(myenv, _argv[1]);
putenv(myenv);//增加环境变量到父进程中
return 1;
}
else if(_argc == 2 && strcmp(_argv[0], "echo") == 0)
{
if(strcmp(_argv[1],"$?") == 0)
{
printf("%d\n", lastcode);
lastcode=0;
}
else if(*_argv[1] == '$')
{
char* val = getenv(_argv[1]+1);
if(val) printf("%s\n", val);
}
return 1;
}
//特殊处理一下
if(strcmp(_argv[0], "ls") == 0)
{
_argv[_argc++] = "--color";
_argv[_argc] = NULL;
}
return 0;
}
int main()
{
while(!quit)
{
//1.交互问题,获取命令行
interact(commandline,sizeof(commandline));
//2.子串分割问题,解析命令行
int argc = splitstring(commandline,argv);
if(argc == 0) continue;
for(int i = 0; i < argc; i++)
{
//printf("%d:",i);
//printf("%s\n",argv[0]);
printf("%s\n",argv[i]);
}
printf("\n");
//指令的判断
//内建命令,本质就是一个shell内部的一个函数
int n = buildCommand(argv, argc);
//3.普通命令的执行
if(!n) NormalExcute(argv);
}
return 0;
}
代码运行的结果如下:
在执行普通命令之前,我们需要判断是不是内建命令,如果是的话,分析之后让父进程执行!
6.myshell代码
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define LINE_SIZE 1024
#define DELIM " "
#define ARGC_SIZE 32
#define EXIT_CODE 44
int quit=0;
char pwd[LINE_SIZE];
char commandline[LINE_SIZE];
char* argv[ARGC_SIZE];
int lastcode = 0;
//自定义环境变量表
char myenv[LINE_SIZE];
//自定义本地变量表
const char* getusername()
{
return getenv("USER");
}
//const char* gethostname()
//{
// return getenv("HOSTNAME");
//}
const char* mygethostname()
{
//char myhostname[LINE_SIZE];
return getenv("HOSTNAME");
}
void getpwd()
{
getcwd(pwd, sizeof(pwd));
}
void interact(char* cline, int size)
{
//获取主机名
char hostname[256];
gethostname(hostname, sizeof(hostname));
//获取当前路径
getpwd();
//printf("%s\n",mygethostname());
//printf(LEFT"%s@%s%s"RIGHT""LABLE" ",getusername(),mygethostname(),pwd);
printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(),hostname, pwd);
char* s = fgets(cline, size, stdin);
assert(s);//debug版本下assert才起效果
//release,assert失效
(void)s;//使用一下,可以在不同版本都生效,抵挡编译器报警
//"abcd\n\0"
cline[strlen(cline)-1] = '\0';
}
int splitstring(char cline[], char* _argv[])
{
int i = 0;
_argv[i++] = strtok(cline, DELIM);
//stork函数扫描字符串末尾时,返回空指针
while(_argv[i++] = strtok(NULL,DELIM));//故意写的=
return i - 1;
}
void NormalExcute(char* _argv[])
{
pid_t id = fork();
if(id < 0)
{
perror("fork");
return;
}
else if(id == 0)
{
//让子进程执行命令
execvp(_argv[0],_argv);
exit(EXIT_CODE);
}
else
{
//创建子进程失败
int status = 0;
pid_t rid = waitpid(id, &status,0);
if(rid == id)
{
lastcode = WEXITSTATUS(status);
}
}
}
int buildCommand(char* _argv[], int _argc)
{
if(_argc == 2 && strcmp(_argv[0], "cd") == 0)
{
//修改父进程中的当前路径
chdir(argv[1]);
//把修改路径放到pwd数组中
getpwd();
//把pwd数组的路径,放到环境变量中
sprintf(getenv("PWD"), "%s", pwd);
return 1;
}
else if(_argc == 2 && strcmp(_argv[0], "export") == 0)
{
strcpy(myenv, _argv[1]);
putenv(myenv);//增加环境变量到父进程中
return 1;
}
else if(_argc == 2 && strcmp(_argv[0], "echo") == 0)
{
if(strcmp(_argv[1],"$?") == 0)
{
printf("%d\n", lastcode);
lastcode=0;
}
else if(*_argv[1] == '$')
{
char* val = getenv(_argv[1]+1);
if(val) printf("%s\n", val);
}
return 1;
}
//特殊处理一下
if(strcmp(_argv[0], "ls") == 0)
{
_argv[_argc++] = "--color";
_argv[_argc] = NULL;
}
return 0;
}
int main()
{
while(!quit)
{
//1.交互问题,获取命令行
interact(commandline,sizeof(commandline));
//2.子串分割问题,解析命令行
int argc = splitstring(commandline,argv);
if(argc == 0) continue;
//指令的判断
//内建命令,本质就是一个shell内部的一个函数
int n = buildCommand(argv, argc);
//3.普通命令的执行
if(!n) NormalExcute(argv);
}
return 0;
}