模拟Linux的shell

在学习了Linux的进程控制之后,学习了fork函数和exec函数族,通过这些个函数可以简单的实现一份shell,就是实现一份命令行解释器,当然是简单版的,实现功能如下

  1. 能执行普通的命令如ls ,ps ,top等
  2. 可以实现目录的跳转cd命令
  3. 能执行命令并加上参数如ls-l
  4. 能执行打开man手册
  5. 能识别管道符

还不能实现正则表达式,要实现这个我当前的代码根本不能用,要重头开始改写。。。

下面贴代码

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 #include <stdlib.h>
  7 #include <pwd.h>
  8 #include <sys/utsname.h>
  9 #include <libgen.h>
 10
 11
 12 void eatblank(char **buf)
 13 {
 14   while(**buf==‘ ‘)
 15   {
 16     (*buf)++;
 17   }
 18 }
 19
 20
 21 void GetHostName(char *hostname,int length)
 22 {
 23   gethostname(hostname,length);
 24   char *p=hostname;
 25   while(*p!=‘\0‘)
 26   {
 27     if(*p==‘.‘)
 28     {
 29       *p=‘\0‘;
 30     }
 31     p++;
 32   }
 33 }
 34
 35 void Pipe(char **my_argv,char *buf);
 36 void BuildCommand(char **my_argv,char *buf)
 37 {
 38   eatblank(&buf);
 39   my_argv[0]=buf;
 40   int index=1;
 41   char *p=buf;
 42       while(*p!=‘\0‘)
 43       {
 44         if(*p==‘ ‘)
 45         {
 46           *p=‘\0‘;
 47           p++;
 48           eatblank(&p) ;
 49           if(*p!=‘|‘)
 50           {
 51             my_argv[index++]=p;
 52           }
 53           continue;
 54         }
 55         else if(*p==‘|‘)
 56         {
 57           p++;
 58           //p++;
 59           my_argv[index]=NULL;
 60           Pipe(my_argv,p);
 61         }
 62         else
 63         {
 64           p++;
 65         }
 66       }
 67      my_argv[index]=NULL;
 68 }
 69
 70
 71
 72 void Pipe(char ** my_argv,char *buf)
 73 {
 74   int fd[2];
 75   pipe(fd);
 76   pid_t id2=fork();
 77   if(id2==0)
 78   {
 79     close(1);
 80     dup(fd[1]);
 81     close(fd[1]);
 82     close(fd[0]);
 83     execvp(my_argv[0],my_argv);
 84   }
 85   else
 86   {
 87     waitpid(id2,NULL,0);
 88     close(0);
 89     dup(fd[0]);
 90     close(fd[0]);
 91     close(fd[1]);
 92     BuildCommand(my_argv,buf);
 93     execvp(my_argv[0],my_argv);
 94   }
 95   //在此处添加exec族函数
 96 }
 97
 98
 99 int main()
100 {
101   while(1)
102   {
103     char *my_argv[64];
104       struct passwd *pwd=getpwuid(getuid());
105       char hostname[256]={‘\0‘};
106       char cwd[256]={‘\0‘};
107       getcwd(cwd,256);
108       GetHostName(hostname,256);
109       printf("[%[email protected]%s %s]#",pwd->pw_name,hostname,basename(cwd));
110       fflush(stdout);
111       char buf[1024];
112       buf[0]=‘\0‘;
113
114       int count=read(0,buf,sizeof(buf));
115       buf[count-1]=‘\0‘;
116       my_argv[0]=buf;
117     pid_t id=fork();
118     if(id==0)
119     {
120       //child
121
122       if(strncmp(buf,"cd",2)==0)
123       {
124         exit(1);
125       }
126       BuildCommand(my_argv,buf);
127      execvp(my_argv[0],my_argv);
128      printf("if the process has some problem ,I should run here\n");
129      exit(0);
130     }
131     else
132     {
133       //father
134       int status=0;
135       wait(&status);
136       if(status==256)
137       {
138         my_argv[0]+=3;
139         chdir(my_argv[0]);
140       }
141     }
142   }
143   return 0;
144 }

通过gethostname获取主机名,通过getcwd获得当前工作目录,通过getpwuid获得当前登录的用户信息

这样命令提示符就完成了;

  • 普通命令的实现

普通命令的实现并不困难,我的目的是让子进程来执行命令,也就是通常的让fork产生的子进程执行exec族函数,然后自己死掉,通过父进程回收资源,循环并创建新的子进程,这就是shell的大框架,其中用execvp函数来实现命令的执行,函数原型就不多说了,查手册就能查到,简单解释一下参数,第一个参数是命令行的字符串,第二是参数是一个字符串数组,从上到下依次存放,命令,参数(可能有多个,一个参数占一个下标),最后用NULL占据一个下标表示结束。

  • cd命令的实现

cd命令的实现有些问题,不是普通命令的实现,就是说简单的使用execvp是不能实现的,因为就算子进程改变了目录之后也会把自己杀死,父进程和子进程之间是不通的(就是说父进程和子进程并不共享环境变量,子进程修改了当前工作目录的环境变量对父进程也没有什么影响),后来使用system来执行系统调用,也失败,因为更多时候shell会产生一个子进程来执行命令,因为shell本身执行会有风险,可能会因为用户的错误操作把自己挂掉,所以使用子进程来执行命令,而这样和刚才的结果是一样的并不会影响到父进程,最后采用了chdir函数,他可以改变当前进程的环境变量中的工作目录(就是专门change dir用的),让父进程来执行,fork出来的子进程会有一份父进程环境变量的拷贝,这就完成了改变目录,并将结果传递了下来

 1  else
 2     {
 3       //father
 4       int status=0;
 5       wait(&status);
 6       if(status==256)
 7       {
 8         my_argv[0]+=3;
 9         chdir(my_argv[0]);
10       }
11     }
  • 管道符的实现

管道符的实现很简单,平常我们执行命令的时候,都是结果自动输出到电脑屏幕上,这说明一般命令的输出是write在标准输出stdout的,而我们输出命令的参数是通过键盘,这说明命令的输入来源是标准输入stdin,如果我们关闭了,标准输出和标准输入,而是通过一个管道,让结果写进管道,然后让参数从管道中读取(简单的说就是让管道的两段代替标准输入和标准输出),管道符就实现了

 1 void Pipe(char ** my_argv,char *buf)
 2 {
 3   int fd[2];
 4   pipe(fd);
 5   pid_t id2=fork();
 6   if(id2==0)
 7   {
 8     close(1);
 9     dup(fd[1]);
10     close(fd[1]);
11     close(fd[0]);
12     execvp(my_argv[0],my_argv);
13   }
14   else
15   {
16     waitpid(id2,NULL,0);
17     close(0);
18     dup(fd[0]);
19     close(fd[0]);
20     close(fd[1]);
21     BuildCommand(my_argv,buf);
22     execvp(my_argv[0],my_argv);
23   }
时间: 2024-10-25 19:24:02

模拟Linux的shell的相关文章

在Window平台是模拟Linux的Shell环境

在Linux平台模拟Linux的shell环境,可以通过一个软件实现:Cygwin(点击进入官网下载好即可),如下图(选择对应的版本进行下载): 安装: 1. 双击运行下载的安装包(选择从网络安装),如下图: 2. 一路next,选择Direct Connection 3. 下一步选择镜像站点,一般选择国内的镜像站点,如163镜像站就有cymwin.如下图(这里直接选择的第一个站点,速度很快):   4. 下载完之后,进行下一步,选择Current 5. 等待下载完之后,就安装完毕,可以进行使用

Linux下Shell脚本输出带颜色文字

Linux下Shell脚本输出带颜色文字 文本终端的颜色可以使用"ANSI非常规字符序列"来生成. 举例:    "echo -e \033[44;37;5m BLUE \033[0m" "echo -e \033[44;37;5m BLUE \033[0m BLACK" 需要修改的地方是红色带下划线的部分,其中[***m部分的内容换成下面的数字,后面的内容是自己想要上色的内容. 以上命令设置作用如下:背景色为蓝色,前景色为白色,字体闪烁,输出字

linux 模仿shell操作

linux的文件系统,是把硬盘空间格式化为若干node和block数据块,一般文件的创建,目录的创建,都会占用一个node,如果创建的是文件,具体的数据保存在若干block数据块中,node记录数据块的号码,所有的node和block在文件系统中都已经编号好:如果创建的是目录,会占用一个node,一个block,block中记录着该目录中所有的文件名,block号码记录在node中. 根据linux文件系统的大体结构,用文件的方式,采用C++语言来模拟一些在shell中执行的命令:cd.mkdi

Linux Bash Shell编程快速入门

BASH 的基本语法 最简单的例子 -- Hello World! 关于输入.输出和错误输出 BASH 中对变量的规定(与 C 语言的异同) BASH 中的基本流程控制语法 函数的使用 2.1     最简单的例子 -- Hello World! 几乎所有的讲解编程的书给读者的第一个例子都是 Hello World 程序,那么我们今天也就从这个例子出发,来逐步了解 BASH. 用 vi 编辑器编辑一个 hello 文件如下: #!/bin/bash# This is a very simple

Linux的shell中echo改变输出显示样式

Linux的shell中echo改变输出显示样式 echo -e "\033[32;49;1m [DONE] \033[39;49;0m" 或echo -e "\e[32;49;1m [DONE] \033[39;49;0m" 输出结果 :[DONE] 文本终端的颜色可以使用“ANSI非常规字符序列”来生成.举例: echo -e "\033[44;37;5m ME \033[0m COOL" 以上命令设置背景成为蓝色,前景白色,闪烁光标,输出字

Linux Bash Shell入门教程

BASH 的基本语法 最简单的例子 -- Hello World! 关于输入.输出和错误输出 BASH 中对变量的规定(与 C 语言的异同) BASH 中的基本流程控制语法 函数的使用 2.1     最简单的例子 -- Hello World! 几乎所有的讲解编程的书给读者的第一个例子都是 Hello World 程序,那么我们今天也就从这个例子出发,来逐步了解 BASH. 用 vi 编辑器编辑一个 hello 文件如下: #!/bin/bash # This is a very simple

linux下shell脚本执行方法及exec和source命令

exec和source都属于bash内部命令(builtins commands),在bash下输入man exec或man source可以查看所有的内部命令信息. bash shell的命令分为两类:外部命令和内部命令.外部命令是通过系统调用或独立的程序实现的,如sed.awk等等.内部命令是由特殊的文件格式(.def)所实现,如cd.history.exec等等. 在说明exe和source的区别之前,先说明一下fork的概念. fork是linux的系统调用,用来创建子进程(child

Linux系统shell脚本编程——生产实战案例

Linux系统shell脚本编程--生产实战案例     在日常的生产环境中,可能会遇到需要批量检查内网目前在线的主机IP地址有哪些,还可能需要检查这些在线的主机哪些端口是开放状态,因此依靠手工来检查是可以实现,但比较费时费力,所以需要结合shell脚本来实现批量检查的功能,那么今天就来做个小小的实验. 1.开发脚本前准备 一般大家都知道,测试主机是否在线,常用的命令无非就是ping.nmap,因此,首先找一个地址来测试下ping命令的效果 [[email protected] scripts]

Linux/Unix shell sql 之间传递变量

灵活结合Linux/Unix Shell 与SQL 之间的变量传输,极大程度的提高了DBA的工作效率,本文针对Linux/Unix shell sql 之间传递变量给出几个简单的示例以供参考. Linux/Unix 下调用SQL,RAMN 请参考:Linux/Unix shell 脚本中调用SQL,RMAN脚本 一.示例 [python] view plain copy print? 1.shell变量接受sql返回值之方式一 [email protected]:~> more ./retval