写在最前面:其实这是一个上课作业,我觉得我不能就这么简单的实现了而不给后人留下点什么糟粕。所以下面有兴趣的同学就跟着我误入歧途吧。我没想按着APUE这本书一章一章来,我认为既然是我自己读完了这本书并要实现东西,就需要有我自己的节奏,这种节奏不仅仅让我自己,也要让各位看客感到轻松,并偶有所获~我就十分开心了。
Preface
这学期报了一门课叫《Unix环境高级编程》,本来打算水水过去的,没想到老师需要我们每个人完成一份大作业,作业内容就是艰深复杂的各种调用底层API实现的shell。
怎么说呢?从严格意义上,操作系统可以定义为一种软件,它控制计算机硬件资源,提供程序运行环境,我们将这种软件成为内核(也就是kernel),内核的接口被称为系统调用,公用函数库构建在系统调用接口之上,应用程序既可以使用公用函数库,也可以使用系统调用。shell是一个特殊的应用程序,为运行其他程序提供了一个接口。
我们要实现的shell就是这么一个东西。
这么大段的定义还不如说【oh-my-zsh,zsh,bash】 来的实在,以上三种就是我们常见的shell,我现在最喜欢的就是 oh-my-zsh 了,其优点太多了,tabtab可以自动提示参数,配色好看几百倍等等等等。
废话不多说,要开车了哦。
准备工作
首先
一台Linux系统的计算机(比较方便,在windows下需要配置很多的环境,因为windows没有Unix的很多库函数,我使用的是 Ubuntu 16.04)
下载apue源码 传送门
解压源码文件 tar -zxvf *.tar.gz
cd apue.3e/ & make
在这个过程中,你会看到最后由于can,t find-lbsd而不能make成功,解决办法是添加libbsd.a的静态链接库
sudo apt-get install libbsd-dev
5. 重新 make 然后再
sudocp ./include/apue.h /usr/include/ sudocp ./lib/libapue.a /usr/local/lib/
有时候以上步骤完成后就可以随便找书上的例子开始装逼了,但是我的电脑不行,因为里面有很多err_sys之类的报错函数未定义,那么还需要再执行一步。
复制下面的代码,并保存成 /usr/include/myerr.h
以后你写的例子里需要用到err_sys之类的函数,你需要 #include "myerr.h"
#include "apue.h" #include <errno.h> /* for definition of errno */ #include <stdarg.h> /* ISO C variable aruments */ static void err_doit(int,int,const char *,va_list); /* * Nonfatal error related to a system call. * Print a message and return. */ void err_ret(const char *fmt,...) { va_list ap; va_start(ap,fmt); err_doit(1,errno,fmt,ap); va_end(ap); } /* * Fatal error related to a system call. * Print a message and terminate. */ void err_sys(const char *fmt,ap); va_end(ap); exit(1); } /* * Fatal error unrelated to a system call. * Error code passed as explict parameter. * Print a message and terminate. */ void err_exit(int error,const char *fmt,error,ap); va_end(ap); exit(1); } /* * Fatal error related to a system call. * Print a message,dump core,and terminate. */ void err_dump(const char *fmt,ap); va_end(ap); abort(); /* dump core and terminate */ exit(1); /* shouldn't get here */ } /* * Nonfatal error unrelated to a system call. * Print a message and return. */ void err_msg(const char *fmt,fmt); err_doit(0,ap); va_end(ap); } /* * Fatal error unrelated to a system call. * Print a message and terminate. */ void err_quit(const char *fmt,ap); va_end(ap); exit(1); } /* * Print a message and return to caller. * Caller specifies "errnoflag". */ static void err_doit(int errnoflag,int error,va_list ap) { char buf[MAXLINE]; vsnprintf(buf,MAXLINE,ap); if (errnoflag) snprintf(buf+strlen(buf),MAXLINE-strlen(buf),": %s",strerror(error)); strcat(buf," "); fflush(stdout); /* in case stdout and stderr are the same */ fputs(buf,stderr); fflush(NULL); /* flushes all stdio output streams */ }
尾声
既然是第一章,当然我们轻松点,那么就来一个最最开始的1-7程序吧,这个程序搭建了最基本的功能,也就是输入,连命令也不是去解析调API而是直接用原始bash的命令~
// // Created by jasperyang on 17-6-6. // #include "apue.h" #include <sys/wait.h> #include "myerr.h" int main(void) { char buf[MAXLINE]; /* from apue.h */ pid_t pid; int status; printf("%% "); /* print prompt (printf requires %% to print %) */ while(fgets(buf,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 */ execlp(buf,buf,(char*)0); err_ret("couldn't execute: %s",buf); exit(127); } /* parent */ if((pid = waitpid(pid,&status,0)) < 0) err_sys("waitpid error"); printf("%% "); } exit(0); }
简单易懂老少皆宜,这段程序输入命令,并fork一个child去执行命令,执行命令用的是execlp这个函数,执行失败与否子程序都exit(127),127倒没什么特殊的含义。
原文链接:https://www.f2er.com/bash/392849.html