如何写一个简单的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( ...
随机推荐
- javascript中无法通过div.style.left获取值的问题
一.问题总结: 样式必须直接写在元素内部才能通过div.style.left直接获取属性值(也就是必须是内联样式才行),定义在css中的样式不能通过这种方式获取. 让元素移动到200停止 setTim ...
- HDU 2222 关键词查找
题目大意:给出一篇文章,长度最多1000000,若干个关键词,关键词有可能重复.关键词不超过10000,每个关键词不超过50个字符.请问该文章包含多少个关键词. 这是AC自动机的入门题.首先将关键词分 ...
- (WPF) 基本题
What is WPF? WPF (Windows Presentation foundation) is a graphical subsystem for displaying user inte ...
- OAF_开发系列06_实现OAF属性集的介绍和开发Attribute Set(案例)
20150705 Created By BaoXinjian
- 【javascript 进阶】异步调用
前言 javascript的中的异步是很重要的概念,特别是ajax的提出,给整个web带来了很大的影响,今天就介绍下javascript的异步编程. 同步与异步 何为同步?何为异步呢? 同步:说白了就 ...
- BC之jrMz and angles
jrMz and angles Accepts: 594 Submissions: 1198 Time Limit: 2000/1000 MS (Java/Others) Memory Lim ...
- 设计一个Stack,要求Push、Pop、获取最大最小值时间复杂度都为O(1)
面试的时候,面试官让设计一个栈,要求有Push.Pop和获取最大最小值的操作,并且所有的操作都能够在O(1)的时间复杂度完成. 当时真没啥思路,后来在网上查了一下,恍然大悟,只能恨自己见识短浅.思路不 ...
- OpenLayers 3 基础知识(一)
OpenLayers 是一个专为Web GIS 客户端开发提供的JavaScript 类库包,用于实现标准格式发布的地图数据访问. 要在你的网页中使用OpenLayers(现用版本:v3.19,1), ...
- 线程,join合并线程
在A中,调用B.join()那么,A会“等”B先执行 public class Join01 extends Thread { @Override public void run() { int su ...
- HttpContextBase转换成HttpContext对象
有以下方法: 主要是方式就是通过context获取HttpApplication,然后通过Application获取相应的HttpContext ①HttpContext context=HttpCo ...