遨游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;
}
休息一下,下一章见~ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
