微信 微网站开发,设计方案怎么写格式,完美世界建模培训,青岛cms建站系统ok#xff0c;考完试成功复活
这次是自己的shell命令程序的示例 流程图#xff1a; 关键函数
1.void print_prompt()
函数说明#xff1a;这个函数打印myshell提示符#xff0c;即“myshell$$”.
2.void get_input(char *buf)
函数说明#xff1a;获得一条指令#…ok考完试成功复活
这次是自己的shell命令程序的示例 流程图 关键函数
1.void print_prompt()
函数说明这个函数打印myshell提示符即“myshell$$”.
2.void get_input(char *buf)
函数说明获得一条指令buf用来存放输入的命令。命令过长会终止程序以换行符\n作为结束
3.void explain_input(char *buf,int *argcount,char arglist[100][256])
函数说明解析buf中存放的命令把选项放在arglist中,同时把argcount的值改成选项的个数。比如“ls -l /tmp”,则arglist[0],arglist[1].arglist[2]分别是ls,-l,/tmp。
4.do_cmd(int argcount,char arglist[100][256])
函数说明执行命令
5.int find_command(char *command)
函数说明功能是分别在当前目录/bin目录/usr/bin目录下查找命令的可执行程序。
函数源码
准备代码
首先是一些头文件和定义的代码
#includestdio.h
#includestdlib.h
#includeunistd.h
#includestring.h
#includesys/types.h
#includesys/wait.h
#includefcntl.h
#includesys/stat.h
#includedirent.h
//头文件里都是以前出现过的我就不再说每个的用处了
#define normal 0 //一般的命令
#define out_redirect 1 //输出重定向
#define in_redirect 2 //输入重定向
#define have_pipe 3 //命令中有管道
void print_prompt();//打印提示符
void get_input(char *);//得到输入的命令
void explain_input(char *,int *,char [][256]);//对输入的命令进行解析
void do_cmd(int,char [][256]);//执行命令
int find_command(char *);//查找命令中的可执行程序
main函数
因为这个程序模块化的很清楚直接看main函数不会看不懂
int main(int argc,char **argv){int i;int argcount0;//这个是存选项数的char arglist[100][256];//这个是存选项的char **argnullptr;char *bufnullptr;//这个是得到输入的数组//bufnew char[256];buf(char *)malloc(256);//用C的malloc声明if(bufnullptr){perror(malloc failed);//perror也出现过好多次了详见我以前写的内容exit(-1);}while (1)//循环读入命令{memset(buf,0,256);//先把buf清空print_prompt();//打印提示符get_input(buf);//读入命令//如果是exit或logout就退出if(strcmp(buf,exit\n)0||strcmp(buf,logout\n)0)break;for(i0;i100;i)//把arglist清空arglist[i][0]\0;argcount0;explain_input(buf,argcount,arglist);//通过这个函数去解释buf里面的命令函数会更新argcount和arglistdo_cmd(argcount,arglist);//执行命令}if(buf!nullptr){//退出后记得清空内存free(buf);bufnullptr;}exit(0);
}
print_prompt函数
最简单的一集打印提示符就完了
void print_prompt(){printf(myshell$$ );
}
get_input函数
void get_input(char *buf){int len0;char ch;//古法读入字符串长度最长是256遇到换行停止chgetchar();while (len256ch!\n){buf[len]ch;chgetchar();}if(len256){printf(command is too long \n);exit(-1);}buf[len]\n;len;buf[len]\0;//记得最后添个\0
}
explain_input函数
void explain_input(char *buf,int *argcount,char arglist[][256]){//解释后的结果会存到arglist里//具体解释的方式就是根据空格分割分割后的每个字符串存到arglist里//也就C语言没有split函数不然至于这么麻烦吗...char *pbuf;char *qbuf;int number0;while (1){if(p[0]\n) break;if(p[0] ) p;//首先跳过空格走到一个字符串的开头else{qp;//然后指针q指向这个字符串的开头用q来遍历字符串直到下一个空格或者换行换行代表结束number0;while (q[0]! q[0]!\n){number;//顺便记录字符串的长度其实不用number也行q-p不就得了书上反正用了numberq;}//循环后现在p是字符串的开头q是字符串结尾的下一个空格位置strncpy(arglist[*argcount],p,number1);//把分割的字符串赋值给arglistarglist[*argcount][number]\0;//C风格的字符串记得结尾加\0(*argcount);选项数加一pq;//p直接跳到q的位置继续循环}}
}
do_cmd函数
这个函数才是关键好吧执行输入的指令
void do_cmd(int argcount,char arglist[][256]){int flag0;//重定向的标志int how0;//具体是什么重定向取值是define的那些值int background0;//标志是否有后台运行符 int status;//这是给waitpid用的int i;int fd;//这是文件描述符char *arg[argcount1];char *argnext[argcount1];//这是给管道用的char *file;//文件名pid_t pid;//进程号for(i0;iargcount;i){arg[i](char *)arglist[i];//首先先把arglist的值复制到arg里面其实我也不是很明白为什么要重开一个数组}arg[argcount]nullptr;//数组多开的那个放空指针//查看命令是否有后台运行符for(i0;iargcount;i){if(strncmp(arg[i],,1)0){//看一下最开始的函数描述这个示例代码的后台运行符只能出现在最后if(iargcount-1){background1;arg[argcount-1]nullptr;break;}else{//所以如果不是出现在最后就提示命令错误printf(wrong command\n);return;}}}for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){flag;howout_redirect;if(arg[i1]nullptr)flag;}if(strcmp(arg[i],)0){flag;howin_redirect;if(i0)flag;}if(strcmp(arg[i],|)0){flag;howhave_pipe;if(arg[i1]nullptr)flag;if(i0)flag;}}if(flag1){printf(wrong command\n);return;}if(howout_redirect){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){filearg[i1];arg[i]nullptr;}}}if(howin_redirect){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){filearg[i1];arg[i]nullptr;}}}if(howhave_pipe){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],|)0){arg[i]nullptr;int j;for(ji1;arg[j]!nullptr;j)argnext[j-i-1]arg[j];argnext[j-i-1]arg[j];break;}}}if((pidfork())0){printf(fork error\n);return;}switch (how){case 0:if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}execvp(arg[0],arg);exit(0);}break;case 1:if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fdopen(file,O_RDWR|O_CREAT|O_TRUNC,0644);dup2(fd,1);execvp(arg[0],arg);exit(0);}break;case 2:if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fdopen(file,O_RDONLY);dup2(fd,0);execvp(arg[0],arg);exit(0);}break;case 3:if(pid0){pid_t pid2;int status2;int fd2;if((pid2fork())0){printf(fork2 error\n);return;}else if(pid20){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fd2open(/tmp/youdonotknowfile,O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd2,1);execvp(arg[0],arg);exit(0);}if(waitpid(pid2,status2,0)-1)printf(wait for child process error\n);if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fd2open(/tmp/youdonotknowfile,O_RDONLY);dup2(fd,0);execvp(argnext[0],argnext);if(remove(/tmp/youdonotknowfile))printf(remove error);exit(0);}break;default:break;}if(background1){printf([process id %d]\n,pid);return;}if(waitpid(pid,status,0)-1)printf(wait for child process eerror\n);
}
代码里没有注释的地方是在这里具体说明单靠注释应该还是很难说清的
首先是这段代码
//查看命令里是否有重定向符号for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){flag;howout_redirect;if(arg[i1]nullptr)flag;}if(strcmp(arg[i],)0){flag;howin_redirect;if(i0)flag;}if(strcmp(arg[i],|)0){flag;howhave_pipe;if(arg[i1]nullptr)flag;if(i0)flag;}}if(flag1){printf(wrong command\n);return;}
首先要说明本程序只能支持处理一个重定向符所以既有输入重定向又有输出重定向是错误的这是最底下flag1的一个意思即重定向符太多了。
但是代码里还有其他地方也有flag的操作这是为什么
这需要一点点linux重定向的知识。你光写个是没用的肯定是要加文件 比如这样的命令
echo hello test.txt
所以像 后面就不能是空了
还有像管道符它肯定不能是命令的第一个。
不过这个示例代码里我怀疑它是故意的也不能是第一个吧后面我也不知道可以不跟文件名的命令所以最好是把管道那段的判断替换和。
所以如果出现这种错误flag就会超过1然后报命令格式错误。 然后是这里
if(howout_redirect){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){filearg[i1];//得到文件名arg[i]nullptr;}}}if(howin_redirect){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){filearg[i1];//得到文件名arg[i]nullptr;}}}if(howhave_pipe){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],|)0){arg[i]nullptr;int j;for(ji1;arg[j]!nullptr;j)//把管道符后面的全都存到argnext里来argnext[j-i-1]arg[j];argnext[j-i-1]arg[j];break;}}}
这段还是能理解的主要是获得文件名文件名 自然是跟在重定向符后面了。你看这里的后面就跟文件名了所以我说上面有问题
这里设置arg[i]nullptr我估计是为了减少遍历吧示例代码里for循环的终止条件都是arg[i]!nullptr 处理完这部分在此处设为nullptr这样后面遍历就不用遍历到这里了、
对于管道像一般的管道命令echo Hello, World! | grep Hello
实际上相当于两个shell命令所以会用到子进程来操作实际上整个命令的处理都是要用子进程那么就要单独把管道符后面的命令先存起来了 最后是这么一长串 //创建子进程if((pidfork())0){printf(fork error\n);return;}switch (how)//根据how来进行不同的处理{case 0://命令符中不含重定向和管道if(pid0){//pid0是子进程if(!(find_command(arg[0]))){//看这个命令是不是系统有的printf(%s:command not fount\n,arg[0]);exit(0);}execvp(arg[0],arg);//***exit(0);}break;case 1://有输出重定向if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fdopen(file,O_RDWR|O_CREAT|O_TRUNC,0644);//向文件里写dup2(fd,1);execvp(arg[0],arg);exit(0);}break;case 2://有输入重定向if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fdopen(file,O_RDONLY);//只需要从文件里读所以只用O_RDONLYdup2(fd,0);execvp(arg[0],arg);exit(0);}break;case 3://有管道if(pid0){pid_t pid2;int status2;int fd2;//再创建一个子进程if((pid2fork())0){printf(fork2 error\n);return;}else if(pid20){//这是管道的子进程了if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fd2open(/tmp/youdonotknowfile,O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd2,1);execvp(arg[0],arg);exit(0);}if(waitpid(pid2,status2,0)-1)printf(wait for child process error\n);if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fd2open(/tmp/youdonotknowfile,O_RDONLY);dup2(fd,0);execvp(argnext[0],argnext);if(remove(/tmp/youdonotknowfile))printf(remove error);exit(0);}break;default:break;}if(background1){printf([process id %d]\n,pid);return;}if(waitpid(pid,status,0)-1)printf(wait for child process error\n);
}
最主要的代码无疑是execvp(arg[0],arg);这是什么意思呢
其实在《Linux C编程实战》笔记进程操作之退出执行等待-CSDN博客 出现过不过还是稍微解释一下
execvp是让子进程执行另一个程序那这个程序是啥就是我们给出的参数arg[0]所指定的可执行文件比如说
char *args[] {ls, -l, NULL}; execvp(ls, args);
那子进程就会执行ls命令。
那arg[0]其实就是我们的指令了arg也是我们指令的具体参数所以子进程会帮我们执行指令。
还要再解释一下execvp会先在当前路径下找有没有名为arg[0]的可执行文件,没有的话会在环境变量下找而默认的环境变量是包含/bin的所以为什么我们要先find_command。而像shell命令都是已经存好了的无需我们再写一个。这样执行execvp会到/bin下执行一般的shell命令。 dup2函数在《Linux C编程实战》笔记一些系统调用-CSDN博客有讲解不知道的可以去看看
当然如果连open调用也不知道的话可以看这里《Linux C编程实战》笔记文件读写-CSDN博客 有个疑问是怎么就实现了输入输出的重定向
dup2(fd, 0): 这一行代码将文件描述符 fd 复制到标准输入文件描述符 0 上。这意味着标准输入现在被重新定向到你打开的文件 file。
答案就在这个dup2里0是默认的标准输入文件描述符自然1是标准输出文件描述符。我们的fd已经打开了指定的文件然后通过dup2就指定了程序的标准输入输出重定向
如果你想将程序的输出重定向到一个文件你可以使用类似如下的代码
int fd open(output.txt, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd -1) {perror(open);exit(EXIT_FAILURE);
}// 将标准输出重定向到文件描述符 fd
dup2(fd, 1);// 执行输出操作结果将写入 output.txt 文件
printf(This will be written to output.txt\n);// 关闭文件描述符 fd
close(fd);虽然execvp更改了子进程执行的程序但是标准输入输出是保持的这也是为什么在输入重定向的例子中通过 dup2 将文件描述符复制到标准输入后execvp 执行的新程序将从这个文件描述符读取输入 至于管道那部分实际上就是创建了一个子进程让子进程先执行管道符|前面部分的命令同时使用输出重定向把结果放到一个临时文件里等子进程结束再在父进程执行|后面的命令同时使用输入重定向从那个临时文件里读取之前运行的结果。最后把临时文件删除了。
至于waitpid这个函数直接看《Linux C编程实战》笔记进程操作之退出执行等待-CSDN博客有讲内容太多我就不复述了。 find_command函数
主要就是目录的遍历
目录遍历不会的看这里《Linux C编程实战》笔记目录操作-CSDN博客
int find_command(char *command){DIR *dp;struct dirent *dirp;const char *path[]{./,/bin,/usr/bin,nullptr};//从这几个目录里找//这个意思是像 ./a.out这种命令就不用看./了所以加了2if(strncmp(command,./,2)0)commandcommand2;int i0;while (path[i]!nullptr){if((dpopendir(path[i]))nullptr)printf(can not open /bin \n);while ((dirpreaddir(dp))!nullptr){if(strcmp(dirp-d_name,command)0){//找到了closedir(dp);return 1;}}closedir(dp);i;}return 0;//没找到
}
最后打了半天的代码也是幸好能成功运行 源码copy
最后把所有代码一整个都放在这不过是没有注释的方便大家直接copy
#includestdio.h
#includestdlib.h
#includeunistd.h
#includestring.h
#includesys/types.h
#includesys/wait.h
#includefcntl.h
#includesys/stat.h
#includedirent.h
#define normal 0
#define out_redirect 1
#define in_redirect 2
#define have_pipe 3
void print_prompt();
void get_input(char *);
void explain_input(char *,int *,char [][256]);
void do_cmd(int,char [][256]);
int find_command(char *);
int main(int argc,char **argv){int i;int argcount0;char arglist[100][256];char **argnullptr;char *bufnullptr;//bufnew char[256];buf(char *)malloc(256);if(bufnullptr){perror(malloc failed);exit(-1);}while (1){memset(buf,0,256);print_prompt();get_input(buf);if(strcmp(buf,exit\n)0||strcmp(buf,logout\n)0)break;for(i0;i100;i)arglist[i][0]\0;argcount0;explain_input(buf,argcount,arglist);do_cmd(argcount,arglist);}if(buf!nullptr){free(buf);bufnullptr;}exit(0);
}
void print_prompt(){printf(myshell$$ );
}
void get_input(char *buf){int len0;char ch;chgetchar();while (len256ch!\n){buf[len]ch;chgetchar();}if(len256){printf(command is too long \n);exit(-1);}buf[len]\n;len;buf[len]\0;
}void explain_input(char *buf,int *argcount,char arglist[][256]){char *pbuf;char *qbuf;int number0;while (1){if(p[0]\n) break;if(p[0] ) p;else{qp;number0;while (q[0]! q[0]!\n){number;q;}strncpy(arglist[*argcount],p,number1);arglist[*argcount][number]\0;(*argcount);pq;}}
}
void do_cmd(int argcount,char arglist[][256]){int flag0;int how0;int background0;int status;int i;int fd;char *arg[argcount1];char *argnext[argcount1];char *file;pid_t pid;for(i0;iargcount;i){arg[i](char *)arglist[i];}arg[argcount]nullptr;for(i0;iargcount;i){if(strncmp(arg[i],,1)0){if(iargcount-1){background1;arg[argcount-1]nullptr;break;}else{printf(wrong command\n);return;}}}for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){flag;howout_redirect;if(arg[i1]nullptr)flag;}if(strcmp(arg[i],)0){flag;howin_redirect;if(i0)flag;}if(strcmp(arg[i],|)0){flag;howhave_pipe;if(arg[i1]nullptr)flag;if(i0)flag;}}if(flag1){printf(wrong command\n);return;}if(howout_redirect){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){filearg[i1];arg[i]nullptr;}}}if(howin_redirect){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],)0){filearg[i1];arg[i]nullptr;}}}if(howhave_pipe){for(i0;arg[i]!nullptr;i){if(strcmp(arg[i],|)0){arg[i]nullptr;int j;for(ji1;arg[j]!nullptr;j)argnext[j-i-1]arg[j];argnext[j-i-1]arg[j];break;}}}if((pidfork())0){printf(fork error\n);return;}switch (how){case 0:if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}execvp(arg[0],arg);exit(0);}break;case 1:if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fdopen(file,O_RDWR|O_CREAT|O_TRUNC,0644);dup2(fd,1);execvp(arg[0],arg);exit(0);}break;case 2:if(pid0){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fdopen(file,O_RDONLY);dup2(fd,0);execvp(arg[0],arg);exit(0);}break;case 3:if(pid0){pid_t pid2;int status2;int fd2;if((pid2fork())0){printf(fork2 error\n);return;}else if(pid20){if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fd2open(/tmp/youdonotknowfile,O_WRONLY|O_CREAT|O_TRUNC,0644);dup2(fd2,1);execvp(arg[0],arg);exit(0);}if(waitpid(pid2,status2,0)-1)printf(wait for child process error\n);if(!(find_command(arg[0]))){printf(%s:command not fount\n,arg[0]);exit(0);}fd2open(/tmp/youdonotknowfile,O_RDONLY);dup2(fd,0);execvp(argnext[0],argnext);if(remove(/tmp/youdonotknowfile))printf(remove error);exit(0);}break;default:break;}if(background1){printf([process id %d]\n,pid);return;}if(waitpid(pid,status,0)-1)printf(wait for child process error\n);
}
int find_command(char *command){DIR *dp;struct dirent *dirp;const char *path[]{./,/bin,/usr/bin,nullptr};if(strncmp(command,./,2)0)commandcommand2;int i0;while (path[i]!nullptr){if((dpopendir(path[i]))nullptr)printf(can not open /bin \n);while ((dirpreaddir(dp))!nullptr){if(strcmp(dirp-d_name,command)0){closedir(dp);return 1;}}closedir(dp);i;}return 0;
}