遨游Unix -- APUE课程笔记【2】
Preface上一篇我们实现了一个最简单的shell,并且这个shell只是去执行了bash的指令,那么我们如果要去实现所有的命令需要怎么做呢?比如ls。 首先,我们就应该想到解析参数,因为只要解析了参数我们就能调用exec函数去执行命令了。 一般来讲, int mian(argc,**argv) 这是最常见的传入命令行参数的方式,那么问题来了,argv是怎么样从string解析出来的呢?需要考虑很多鲁棒性的问题,去空格,取命令等等。下面我们就先来实现怎么取解析输入命令吧。 解析输入命令这里要好好利用strtok这个函数,可以很方便的切分 char[] 类型的字符串。 enum { kMaxArgs = 64 }; int argc = 0; char *argv[kMaxArgs]; // 解析命令成 (argc,**argv) int parse_para(char commandLine[]) { char *p2; p2 = strtok(commandLine," "); while (p2 && argc < kMaxArgs-1) { printf("%sn",p2); argv[argc++] = p2; p2 = strtok(0," "); } argv[argc] = 0; } 其实个人更喜欢 c++ 的做法 #include <vector> #include <string> #include <sstream> std::string cmd = "mycommand arg1 arg2"; std::istringstream ss(cmd); std::string arg; std::list<std::string> ls; std::vector<char*> v; while (ss >> arg) { ls.push_back(arg); v.push_back(const_cast<char*>(ls.back().c_str())); } v.push_back(0); // need terminating null pointer execv(v[0],&v[0]); 不管哪种方式,这样我们每次输入的string就可以转化成argc和**argv了(全局变量) man 3 getopt 可以获得一个例子 getopt() The following trivial example program uses getopt() to handle two program options: -n,with no associated value; and -t val,which expects an associated value. #include <unistd.h> #include <stdlib.h> #include <stdio.h> int main(int argc,char *argv[]) { int flags,opt; int nsecs,tfnd; nsecs = 0; tfnd = 0; flags = 0; while ((opt = getopt(argc,argv,"nt:")) != -1) { switch (opt) { case 'n': flags = 1; break; case 't': nsecs = atoi(optarg); tfnd = 1; break; default: /* '?' */ fprintf(stderr,"Usage: %s [-t nsecs] [-n] namen",argv[0]); exit(EXIT_FAILURE); } } printf("flags=%d; tfnd=%d; nsecs=%d; optind=%dn",flags,tfnd,nsecs,optind); if (optind >= argc) { fprintf(stderr,"Expected argument after optionsn"); exit(EXIT_FAILURE); } printf("name argument = %sn",argv[optind]); /* Other code omitted */ exit(EXIT_SUCCESS); } ok~到此,我们可以解析参数了,那么下一步就是要执行命令,在这里,不得不去介绍Unix的exec函数族,8.10 函数exec详细讲解了。 执行命令8.3节曾提到用fork函数创建新的子进程后,子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程执行的程序完全替代为新程序。 因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用磁盘上的一个新程序替代了当前进程的正文段,数据段,堆段和栈段。 一共有7个不同的exec函数。 #include <unistd.h> int execl(const char *pathname,const char *arg0,... /* (char *)0 */); int execv(const char *pathname,char *const argv[]); int execle(const char *pathname,... /* (char *)0,char *const envp[] */); int execve(const char *pathname,char *const argv[],char *const envp[]); int execlp(const char *filename,... /* (char *)0 */); int execvp(cosnt char *filename,char *const argv[]); int fexecve(int fd,char *const envp[]); 7个函数的返回值:若出错则返回-1,若成功则没有返回值 在APUE中,解释好长的一段,主要集中了三种不同的区别:
其实还有更加详细的分析,但是我也不提太多了,因为我们的目标是星辰大海,不可因小失多。其实我一直认为学习这种大部头的方法就是,你先找定一个方向,比如我要实现一个Jas-shell(我自己取的名 :)),然后利用这本书的知识不断去完善我的shell,在这其中,我不能面面俱到,细致入微,但求大刀阔斧,直指前方。当未来我实现了,刚好也大概过了一遍这本书,我会回头慢慢咀嚼细节,然后update我的作品。 不小心废话了一下,哈哈,半桶水叮当响,各位看客一笑了之~ 好了,下面我贴出一个实例,就是在我们第一章实现的基本shell上改的,至于里面用到的imitate_ls的实现,我放到下一章讲~ 其中的 /home/jasperyang/CLionProjects/Jas-shell/imitate_ls 是我实现的ls没代码贴出来,大家耐心等我下一章~或者你们可以自己实现。 // // Created by jasperyang on 17-6-6. // #include "apue.h" #include <sys/wait.h> #include "myerr.h" static void sig_int(int); /* our signal-catching function */ static int parse_para(char commandLine[]); enum { kMaxArgs = 64 }; int argc=0; //命令行参数个数 char *argv[kMaxArgs]; //命令行参数 int main(void) { char buf[MAXLINE]; /* from apue.h */ pid_t pid; int status; if(signal(SIGINT,sig_int)==SIG_ERR) err_sys("signal error"); printf("%% "); /* print prompt (printf requires %% to print %) */ while(fgets(buf,MAXLINE,stdin) != NULL) { if(buf[strlen(buf) -1] == 'n'){ buf[strlen(buf)-1]=0; /* replace newline with null */ } if((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0){ /* child */ argc = 0; parse_para(buf); printf("%sn",argv[0]); if(!strcmp(argv[0],"ls")) { if (execv("/home/jasperyang/CLionProjects/Jas-shell/imitate_ls",argv) < 0) { printf("execv error: %sn",strerror(errno)); exit(-1); } } else { err_ret("couldn't execute: %s",buf); } exit(127); } /* parent */ if((pid = waitpid(pid,&status,0)) < 0) err_sys("waitpid error"); printf("%% "); } exit(0); } //中断信号 void sig_int(int signo) { printf("interruptn%% "); } // 解析命令成 (argc,**argv) int parse_para(char commandLine[]) { char *p2; p2 = strtok(commandLine," "); } argv[argc] = 0; } 休息一下,下一章见~ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |