如何写一个简单的shell
如何写一个简单的shell
看完《UNIX环境高级编程》后我就一直想写一个简单的shell来作为练习,因为有事断断续续的写了好几个月,如今写了差不多来总结一下。
源代码放在了Github: https://github.com/Broglie/Oh-Shell
简单的分析
我们的shell不像bash那样复杂全面,只是实现其中的一小部分功能:命令历史,命令补全,支持IO重定向和管道。一共分成几个部分:主
函数文件,输出出错信息,解析命令等。
我们打开bash对照着做,首先bash有命令提示符,我们要做的和bash的命令提示符一样。然后我们读取用户输入的命令并使用readline库
将命令添加到历史命令列表。然后我们将命令传递给解析命令函数,解析命令并执行。
输出命令提示符
打开bash后我们看到初始的命令提示符如下图所示:

以root登录后用cd命令切换到别的工作目录后如下图所示:

我们发现bash的命令提示符格式为[用户名]@[主机名]:[当前工作目录][$或#]。可以看出用户的home目录以~表示,而结尾部分的$或#是
指如果当前用户是普通用户,则命令提示符是$;如果当前用户是root用户,则命令提示符为#。
那么该如何实现呢?我们用getpwuid函数获取用户的信息,包括用户名和用户ID;用gethostname函数获取主机名;用getcwd函数获
取当前用户的当前工作目录。有了用户ID我们就能判断当前用户是不是root,因为root用户的用户ID为0。有了当前工作目录我们就能判断
工作目录是不是在用户的home目录下,如果在home目录下我们就将home目录的部分替换成~。这样打印提示符的任务就完成了,此部分代码
的实现在main.c文件中的getPrompt函数中。
命令历史和命令补全
Linux默认保存最后输入的500个命令历史。我们可以用GNU的readline库实现命令历史和命令补全。安装就按其中的说明的步骤进
行就行。readline的库有很多功能,但我们只需要其中的readline函数和add_history函数就行了。readline函数以一个字符串为参
数作为提示符,返回用户输入的一行命令;add_history函数以一个字符串作为参数,并将此字符串添加到历史命令列表里。
打印出错消息
在err.h头文件中声明了3个错误处理函数:err_ret函数打印出错消息并返回,err_quit和err_sys函数打印出错消息并退出程序。其
后两个函数在发生致命错误时调用,而err_ret函数在发生非致命错误时调用。其实现在err.c文件中。其实现是参照《UNIX环境高级编程》
中的出错处理函数。
实现IO重定向和管道
处理诸如cat < in.data | grep str | sort > out.data这样的命令需要实现IO重定向和管道。默认情况下在shell中运行的程序其标准输
入(描述符为0)和标准输出(描述符为1)都关联到终端,IO重定向是指将程序的标准输入和标准输出重定向到文件或其他设备。shell从描
述符0读,从描述符1写。如果我们想将标准输入重定向就得关闭描述符0,再将其他文件或设备关联到描述符0。对于标准输出也一样。
管道是将进程的标准输入和/或标准输出通过一个数据通道与另一个进程关联。以本节开头的那个例子来说,grep的标准输入来自cat程
序的输出,标准输出则作为sort程序的标准输入。我们可以用pipe函数创建一个管道,其参数是一个int型数组(假设为fd[2]),数组有
两个元素。经由数组返回两个文件描述符,fd[0]为读而打开,fd[1]为写而打开。fd[1]的输出是fd[0]的输入。如果想重定向标准输入,则将
标准输入与fd[0]相关联,如果想重定向标准输出,则将标准输出与fd[1]相关联。
命令解析
我认为命令解析是这个程序中最难的部分了,可能是我没学过编译原理的原因。所以我死扣了好长时间还是不会怎样解析shll命令(现在也没
搞通)。我偶然发现在xv6课程主页上有一个homework是关于这个的,它给出了解析命令的框架,将关键的部分空出来
让学生补全,所以我就照搬出来用了。所以实现的功能也有限,还有命令列表和后台命令不会解析。等以后我看了编译原理后再把这个坑填了。
总结
这个shell虽然简单,但也让我学到了很多知识,查了很多资料。看完《UNIX环境高级编程》后收获了很多,最高兴的莫过于将学到的知识用在
实际的程序中。就写到这里吧。
Reference:
《UNIX环境高级编程 第三版》
xv6: https://pdos.csail.mit.edu/6.828/2014/xv6.html
如何写一个简单的shell的更多相关文章
- UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell
一 分析 要实现一个shell,需包含3个步骤 1)读入指令 2)指令解析 3)执行指令 1 从键盘读入指令 从键盘读入指令的几个要点: 1)调用getc函数等待并获取用户键盘输入. 2)每一行命令的 ...
- 自动化运维:(3)写一个简单的Shell脚本(案例)
一.需求 1.test.sh 脚本执行时候需要添加参数才能执行 参数和功能详情如下: 参数 执行效果 start 启动中... stop 关闭中... restart 重启中... * 脚本帮助信息. ...
- 写一个简单的 Linux Shell (C++)
这里可以找到代码 github.com/z0gSh1u/expshell 支持的特性 单条指令的执行 引号引起的参数(如 $ some_program "hello, world" ...
- 一步一步写一个简单通用的makefile(三)
上一篇一步一步写一个简单通用的makefile(二) 里面的makefile 实现对通用的代码进行编译,这一章我将会对上一次的makefile 进行进一步的优化. 优化后的makefile: #Hel ...
- Linux系统学习笔记之 1 一个简单的shell程序
不看笔记,长时间不用自己都忘了,还是得经常看看笔记啊. 一个简单的shell程序 shell结构 1.#!指定执行脚本的shell 2.#注释行 3.命令和控制结构 创建shell程序的步骤 第一步: ...
- 单片机裸机下写一个自己的shell调试器
该文章是针对于串口通讯过程中快速定义命令而写的,算是我自己的一个通用化的平台,专门用来进行串口调试用,莫要取笑 要处理串口数据首先是要对单片机的串口中断进行处理,我的方法是正确的命令必须要在命令的结尾 ...
- python定义的一个简单的shell函数的代码
把写代码过程中经常用到的一些代码段做个记录,如下代码段是关于python定义的一个简单的shell函数的代码. pipe = subprocess.Popen(cmd, stdout=subproce ...
- linux设备驱动第三篇:写一个简单的字符设备驱动
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分 ...
- 用Python写一个简单的Web框架
一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...
随机推荐
- mybatis实战教程(mybatis in action)之三:实现数据的增删改查
前面已经讲到用接口的方式编程.这种方式,要注意的一个地方就是.在User.xml 的配置文件中,mapper namespace="com.yihaomen.mybatis.inter.I ...
- springmvc--json--返回json的日期格式问题
(一)输出json数据 springmvc中使用jackson-mapper-asl即可进行json输出,在配置上有几点: 1.使用mvc:annotation-driven 2.在依赖管理中添加ja ...
- oracle之sequence详解
Oracle提供了sequence对象,由系统提供自增长的序列号,每次取的时候它会自动增加,通常用于生成数据库数据记录的自增长主键或序号的地方. sequence的创建需要用户具有create seq ...
- debian下安装zendframework
第一步,打开apache的rewrite模块,因为在debian下使用apache必须执行这一步 a2enmod rewrite #激活rewrite模块 /etc/init.d/apache2 re ...
- flddler使用方法
http://blog.csdn.net/geekgjie/article/details/8029936
- C#位操作(转)
在C#中可以对整型运算对象按位进行逻辑运算.按位进行逻辑运算的意义是:依次取被运算对象的每个位,进行逻辑运算,每个位的逻辑运算结果是结果值的每个位.C#支持的位逻辑运算符如表2.9所示. 算符号 意义 ...
- linux下网站搭建
我们知道windows网站搭建一般是:IIS+Asp+Sqlserver,而 linux网站搭建是:Apache+php+Mysql.两者之间个有千秋,但是为什么我们许多的门户网站搭建都选择linux ...
- SQLServer 创建dtsx包更新统计信息(示例)
http://blog.csdn.net/kk185800961/article/details/43816177(转载) 1 . 打开Microsoft Visual Studio 创建 integ ...
- 基于redis分布式锁实现“秒杀”
转载:http://blog.5ibc.net/p/28883.html 最近在项目中遇到了类似“秒杀”的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓“秒杀”的基本思路. 业务场 ...
- Cookie实现商品浏览记录--方式二:JS实现
使用Cookie实现商品浏览记录:方式二:JS方法实现cookie的获取以及写入.当某一个产品被点击时,触发JS方法.利用JS方法判断一下,此产品是否在浏览记录中.如果不存在,则将产品ID加入到coo ...