shell 是命令行解释器,我们平常在使用操作系统的时候,是不能直接操作内核的,shell可以将用户的命令翻译给kernel处理,同时也可以将核心的处理结果翻译给用户。
我们可以举个例子来理解一下,shell就相当于我们班班花的同桌,我想约班花周末去看电影,但又难以启齿,就让班花的同桌帮我们问班花是否愿意,然后班花的同桌再将班花的回答转达给我,我们只需要把我们要问的告诉班花的同桌,最终得到结果,至于她是怎么给班花说的这个我们不关心。
在Linux中,shell(班花的同桌)主要是对用户的指令(我想约班花周末看电影)进行解析,解析指令给Linux内核(班花),然后再将内核运行之后的结果返回给用户
今天我们就在Linux下实现一个简单的minishell,来完成一些简单的操作
实现minishell的步骤:
通常我们使用fork函数创建一个子进程来执行和父进程一样的程序,但如果向让子进程执行和父进程不一样的代码分支,就需要调用exec函数来将子进程的原有的程序替换成我们想要执行的程序
exec函数族:
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *file, char *const argv[],char *const envp[]);
有p和没有p的区别:要加载的程序是否需要确定给出所在路径
v和l的区别:程序运行参数是函数的参数平铺或者直接组织成为字符串指针数组给与
带e和不带e的区别:要运行的程序是否需要重新定义环境变量
l表示list,代表参数采用列表
v表示vector,代表参数使用数组
p表示path,代表自动搜索环境变量PATH
e表示env,代表需要自己维护环境变量
minishell的实现:
代码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<string.h>
4 #include<stdlib.h>
5 #include<sys/wait.h>
6 #include<ctype.h>
7
8
9 int main() {
10 while(1) {
11 printf("[MXS@localhost]$");
12 fflush(stdout);
13 char tmp[1024]={0};
14 scanf("%[^\n]%*c",tmp);
15 //获取一个连续的字符串%[^\n],从标准输入缓冲区取数据直到遇到\n截止,
16 //%*c:将缓冲区中剩下的\n取出来;否则会造成死循环
17
18 char *ptr=tmp;
19 int argc=0;
20 char *argv[32]={NULL};
21 while(*ptr != '\0') {
22 if(!isspace(*ptr)) {
23 argv[argc]=ptr;
24 argc++;
25 while(!isspace(*ptr) && *ptr != '\0')
26 ptr++;
27 *ptr='\0';
28 ptr++;
29 continue;
30 }
31 ptr++;
32 }
33 argv[argc]=NULL;
34
35 if(!strcmp(argv[0],"cd")){
36 //改变当前工作路径
37 chdir(argv[1]);
38 continue;
39 }
40
41 int pid=fork();
42 if(pid==0) {
43 execvp(argv[0],argv);
44 //若子进程替换程序失败,则直接退出,因为不需要多个shell
45 exit(0);
46 }
47 wait(NULL);
48 }
49 return 0;
50 }
这样我们就实现了一个简单的命令行解释器,需要注意的是shell中处理外部命令是通过创建子进程后程序替换完成功能;还有一部分命令是内建命令,也就是shell自身实现的功能,比如cd改变当前所在路径