📄 myshell.c
字号:
#include<stdio.h>#include<signal.h>#include<unistd.h>#include<string>#include<iostream>#include<sys/types.h>#include<sys/wait.h>#include<fcntl.h>#include<sys/stat.h>using namespace std;void do_cd(char command[]){ int r=0; char path[50]; strcpy(path,command+3); //取目录 if(path!=NULL) r=chdir(path); else cout<<"change dir error.\n"; if(r<0) cout<<"change dir error.\n";}void ex(char command[]) //以整行命令作为参数的exec函数,支持任意多个参数{ char c[50]; memset(c,'\0',50); //初始化c char *argv[50]; int i=0,j=0,k=0,s=0,f=0; while(i<strlen(command)) //对输入的命令根据空格分割 { if(s==0){ //去掉命令开头的空格 while(command[i]==' '){i++;continue;} s=1; } if(command[i]!=' '){c[k]=command[i];f=0;} else{ if(f==1){i++;continue;} //忽略连续空格 else{ argv[j]=(char *)malloc(50); //分配内存空间 strcpy(argv[j],c); j++;k=-1; memset(c,'\0',50); //初始化c; f=1; } } i++;k++; } if(f==0) //结尾没有空格 { argv[j]=(char *)malloc(50); strcpy(argv[j],c); argv[j+1]=NULL; }else argv[j]=NULL; //结尾有空格 execvp(*argv,argv); perror(command); //命令输入错误处理 _exit(0);}void exr(char command[],int f,int p[]) //能处理重定向符号和管道符号的ex函数{ char *txt; int w; pid_t pid; pid = fork(); if(pid==-1) printf("failure!\n "); else if(pid==0) { if(f==1) { close(p[0]); //关闭管道的读端 close(1); //关闭标准输出端子 dup(p[1]); //将stdout重定向到管道写端 } if(f==2) { close(p[1]); //关闭管道的写端 close(0); //关闭标准输入端子 dup(p[0]); //将stdout重定向到管道读端 } if(txt=strstr(command,"<")) //如果有<则直接去掉 { char in_file[20]; *txt='\0'; while(*(txt+1)==' ') txt++; //去掉‘<’后的空格 char *s; if(s=strstr(txt+1," ")) *s='\0'; strcpy(in_file,txt+1); if(s!=NULL) strcat(command,s+1); int fid2; if((fid2=open(in_file,O_RDONLY))<0) //打开用户指定文件,只读打开 fprintf(stderr,"%s ","open file error"); close(0); //关闭标准输入端子 dup(fid2); //将文件in_file作为输入设备 close(fid2); //关闭该文件的连接,释放资源锁让程序来对此 文件进行读写 } if(txt=strstr(command,">")) { char out_file[20]; *(txt-1)='\0'; //将后面重定向部分的命令去掉 while(*(txt+1)==' ') txt++; //去掉‘>’后的空格 strcpy(out_file,txt+1); //取文件名 int filedes1; if((filedes1=open(out_file,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))<0) //打开一个名为out_file的文件,只写打开,不存在则创建,文件表记为可读写 fprintf(stderr,"%s ","open(creat) file error"); close(1); //关闭标准输出端子 dup(filedes1); //将文件out_file作为输出设备 close(filedes1); //关闭该文件的连接,释放资源锁让程序来对此 文件进行读写 } ex(command); }else{ wait(&w); if(f==1)close(p[1]); //关闭管道写端 if(f==2)close(p[0]); //关闭管道读端 }}int main(int argc,char* argv[],char* env[]){ cout<<"********************************************************\n"; cout<<"* myshell *\n"; cout<<"* write by hcy *\n"; cout<<"********************************************************\n"; char command[100]; pid_t pid; int w; while(1) { char *p,*t; p=get_current_dir_name(); //取当前的工作目录 char path[100]; while(t=strstr(p,"/")){*t='0';strcpy(path,t+1);} //取工作文件夹 cout<<"[hcy@localhost "<<path<<"]# "; //打印提示信息 cin.getline(command,100); if(strcmp(command,"show")==0) { pid = fork(); //克隆自己的进程给子进程, 其实就是copy堆栈区和数据给子进程 if(pid==-1) //返回为-1为失败 printf("failure!\n "); else if(pid==0) //因为为了区分父进程与子进程区别,pid不同,子进程中的pid为0 { cout<<"The child process id is "<<getpid()<<endl; //子进程的pid cout<<"The parent process id is "<<getppid()<<endl; //父进程的pid int i; cout<<"\ncommand:"; for(i=0;i<argc;i++) //输出命令行 { cout<<argv[i]<<" "; } i=0; cout<<"\n\n↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓环境变量↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\n\n"; while(env[i]) //输出所有环境变量 { cout<<env[i]<<" "; i++; } cout<<endl; exit(0); } else wait(&w); } else if(strcmp(command,"quit")==0) //退出程序 { cout<<"*************************quit***************************\n"; _exit(0); } else if(strstr(command,"cd")==command) do_cd(command); //如果命令以cd开头则执行do_cd else { char *txt; int p[2]; if(txt=strstr(command,"|")) //如果有管道符号|,则将前面的命令的输出保存在tmp中作为后面命令的输入 { pipe(p); //创建管道 char cmd1[100],cmd2[100]; *txt='\0'; //分割两条命令 strcpy(cmd1,command); strcpy(cmd2,txt+1); exr(cmd1,1,p); //执行前命令 exr(cmd2,2,p); //执行后命令 } else exr(command,0,p); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -