功能如下:
(1)显示命令提示符%;
(2)获取用户输入指令;解析指令;寻找命令文件,执行指令;
(3)前一个命令完成后才继续显示命令提示符%,等待用户输入下一条命令;
(3)如果输入“exit”或“logout”,则退出shell.
(4)允许命令参数间有多个空格,也允许命令前后有空格
(5)支持输出重定向和管道功能。
void _spilt(char **argv,char *file,char **file_argv,char** pipeO,char** pipeI,char *cmdline)
/** 解析从命令行输入的字符串*/
void eval(char* cmdline)
/**执行命令*/
重定向实现:利用dup去改变标准输出文件描述符指向的文件
//核心代码:file是重定向输出的文件,即command > file
fd = open(file,O_WRONLY|O_CREAT|O_TRUNC,0777);
close(1);
dup(fd);
管道实现:利用pipe这个缓冲区(实际上可以当成是文件),然后利用dup改变标准输入和标准输出文件描述符指向的文件
//核心代码:改变标准输入和标准输出文件描述符指向的地方
pipe(pipe_fd);
dup2(pipe_fd[1],1);
dup2(pipe_fd[0],0);
程序所包含的知识:dup和文件描述符,pipe以及exec族(这里用execvp函数实现)还有fork,最后就是解析字符串了
源代码:
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 100
#define LEN 10
int isCDX = 0;
int isPipe = 0;
void _spilt(char **argv,char *file,char **file_argv,char** pipeO,char** pipeI,char *cmdline){
int i = 0, j = 0, cdx_argv, temp,pipe1,pipe2;
int k, len;
len = strlen(cmdline);
isCDX = 0;
isPipe = 0;
//解析shell
for(k = 0;k < len; k++){
if(cmdline[k] == ' '&& k == 0) continue; //去掉首个空格
else if(cmdline[k] == ' '&& cmdline[k-1] == ' ')continue; //去掉多个空格
else if(cmdline[k] == ' '){
//字符串尾
argv[i][j] = '\0';
j = 0;
i++;
}else if(cmdline[k] == '\n' && k > 0 && cmdline[k-1] == ' '){
//字符串解析完多个空格后回车
argv[i] = NULL;
}else if(cmdline[k] == '\n' && k == 0){
//首个就是回车
argv == NULL;
}else if(cmdline[k] == '\n'){
//字符串解析完成
argv[i][j] = '\0';
i++;
argv[i] = NULL;
}else{
//其他情况复制字符
argv[i][j] = cmdline[k];
j++;
}
}
//查找是否为重定向
for(temp = 0; temp <= i; temp++){
if(argv[temp] != NULL){
if(strcmp(argv[temp],">") == 0){
isCDX = 1;
strcpy(file,argv[temp+1]);
for(cdx_argv = 0; cdx_argv < temp; cdx_argv++){
file_argv[cdx_argv] = (char *)malloc(sizeof(char) * MAXLINE);
strcpy(file_argv[cdx_argv],argv[cdx_argv]);
}
file_argv[cdx_argv] = (char *)malloc(sizeof(char) * MAXLINE);
file_argv[cdx_argv] = NULL;
break;
}else continue;
}
}
if(isCDX == 1) return;
//查找是否为管道
for(temp = 0; temp <= i; temp++){
if(argv[temp] != NULL){
if(strcmp(argv[temp],"|") == 0){
isPipe = 1;
for(pipe1 = 0; pipe1 < temp; pipe1++){
pipeO[pipe1] = (char *)malloc(sizeof(char) * MAXLINE);
strcpy(pipeO[pipe1],argv[pipe1]);
}
pipeO[pipe1] = (char *)malloc(sizeof(char) * MAXLINE);
pipeO[pipe1] = NULL;
temp++;
for(pipe2 = 0; temp < i; temp++){
pipeI[pipe2] = (char *)malloc(sizeof(char) * MAXLINE);
strcpy(pipeI[pipe2],argv[temp]);
pipe2++;
}
pipeI[pipe2] = (char *)malloc(sizeof(char) * MAXLINE);
pipeI[pipe2] = NULL;
}
}
}
}
void eval(char* cmdline){
char *argv[LEN];//shell参数
char file[LEN];//输出重定向文件
char *file_argv[LEN];//输出重定向参数
char *pipeI[LEN];//管道接受方command2
char *pipeO[LEN];//管道输入方command1
int i,fd;
pid_t pid,pipePid;
int pipe_fd[2];
for(i = 0; i < LEN; i++){
argv[i] = (char *)malloc(sizeof(char) * MAXLINE);
}
_spilt(argv,file,file_argv,pipeO,pipeI,cmdline);
if(argv[0]==NULL){
return;
}else if(strcmp("exit",argv[0]) == 0 || strcmp("logout",argv[0]) == 0){
exit(0);
}else{
if((pid = fork())==0){
// printf("我在子进程里\n");
if( isCDX ){
//输出重定向
// printf("我在重定向里\n");
fd = open(file,O_WRONLY|O_CREAT|O_TRUNC,0777);
close(1);
dup(fd);
if(execvp(argv[0],file_argv) < 0){
printf("%s:Command not found\n",argv[0]);
exit(0);
}
}else if(isPipe){
//管道
// printf("我在管道里\n");
if(pipe(pipe_fd) == -1){
printf("Pipe error\n");
exit(0);
}else{
pipePid = fork();//在子进程里再创建子进程,这样父进程就不会退出了
if(pipePid == 0){
dup2(pipe_fd[1],1);
if(execvp(pipeO[0],pipeO) < 0){
printf("%s:Command not found\n",pipeO[0]);
exit(0);
}
}else{
int status;
if(waitpid(pipePid,&status,0) < 0){
printf("waitfg : waitpid error\n");
}
else{
dup2(pipe_fd[0],0);
if(execvp(pipeI[0],pipeI) < 0){
printf("%s:Command not found\n",pipeI[0]);
exit(0);
}
}
}
}
}else{
//shell
// printf("我在shell里\n");
if(execvp(argv[0],argv) < 0){
printf("%s:Command not found\n",argv[0]);
exit(0);
}
}
}else{
int status;
if(waitpid(pid,&status,0) < 0){
printf("waitfg: waitpid error\n");
}else{
printf("%d %s",pid,cmdline);
}
}
}
return;
}
int main(){
char cmdline[MAXLINE];
while(1){
printf("%%");
fgets(cmdline,MAXLINE,stdin);
if(feof(stdin)) exit(0);
eval(cmdline);
}
return 0;
}