linux 自己写一个shell,写一个简单的 Linux Shell (C++)

这里可以找到代码

支持的特性

单条指令的执行

引号引起的参数(如 $ some_program "hello, world" )

重定向(>、< )

管道(|)

内建指令(如 cd、history、quit)

指令别名(如 ll → ls -l)

家目录(~)

运行截图

ada51f2eb576a01e4495e5e1970cc177.png

如何写一个简单的 Shell

这里简单介绍写 Shell 时比较关键的一些部分,具体请查看源代码。

展示提示符

见 show_command_prompt 函数。

command_prompt 是在每行最开始显示的一段与用户名、路径等相关的提示信息。ExpShell 显示的 prompt 形如 [root@localhost tmp]>。用 > 而非 #、$ 作为提示符,以区分原生 Shell。

获取用户名

passwd *pwd = getpwuid(getuid());

string username(pwd->pw_name);

获取当前目录

getcwd(char_buf, CHAR_BUF_SIZE);

string cwd(char_buf);

prompt 中目录只显示最近一级,此处用 / 来 split 后取最后一个即可

家目录需要折叠为 ~,这里顺便把家目录地址存到全局变量 home_dir,后续要用到

获取主机名

gethostname(char_buf, CHAR_BUF_SIZE);

string hostname(char_buf);

有时 hostname 会是形如 localhost.locald.xxx 的形式,也 split 处理一下

输出之即可

cout << "[" << username << "@" << hostname << " " << cwd << "]> ";

解析命令

为存储解析结果,定义如下四个类:

cmd:各种 cmd 的基类

exec_cmd:形如 argv[0] argv[1] ... 的普通命令

pipe_cmd:管道命令,形如 left: cmd* | right: cmd*

redirect_cmd:重定向命令,形如 cmd_: cmd* > (or

(最基础的)解析 exec_cmd

见 parse_exec_cmd 函数。注意这里使用 string_split_protect 函数来 split 出 argv,这样可以保持被引号引起的带空格的 argument 不被拆分。

解析一条命令

见 parse 函数。采用分治法递归地解析命令。

从左到右扫描字符串

如果是普通字符,则读入缓存

如果是重定向符号,将当前缓存解析为 exec_cmd,作为左手边 cmd;继续不断读入直到再次遇到符号或字符串结束,作为右手边 file,构建 redirect_cmd

如果是管道符号,递归地调用 parse 解析右侧剩余,解析结果作为本层递归的右手边,构建 pipe_cmd

解析内建命令

主要支持 cd 、history 和 quit 命令。

调用 exit(0) 即可实现 quit

history 命令根据记录打印即可

对于 cd,考虑如下情况

无参 cd 等价于 cd ~

对于形如 cd ~/some_path 的命令,使用 home_dir 替换 ~

其他情况调用 chdir 即可

执行命令

主要见 run_cmd 函数。该函数接收一个 cmd*,递归地完成其链上所有 cmd 的执行。

对于 exec_cmd

检查别名,替换别名,例如 ll → ls -l

使用 execvp 函数执行命令

看 这篇博文 了解 exec 族函数,可见 execvp 在当前场景最为合适

第二个参数是一个末元素为 NULL 的 char**(char*[]),内容为 argv

对于 pipe_cmd

为 pipe_cmd 的 left 和 right 分别 fork 子进程执行,并使用管道让这两个兄弟进程通信

这张图很好地说明了父子进程使用管道通信的方法

ba799520700a37b991d720a157c9bcf6.png

依据上图,不难类比出兄弟进程进行 IPC 的方法如下

父进程 pipe

父进程 fork 两次

child1 关读端,重定向写端,执行命令,关写端

child2 关写端,重定向读端,执行命令,关读端

父进程关闭读、写端,并 wait

对于 redirect_cmd

打开 file,获得 fd

重定向 stdin 或 stdout 到 fd

执行命令

关闭 fd

主函数

在一个死循环中读入当前命令,如果不是 builtin_command,则 fork 子进程进行解析和执行(避免阻塞 ExpShell 自身),执行完成后子进程 exit。

其他细节

pipe、open、dup2 等方法返回值小于 0 均表示出现错误,需要触发 panic

对于 wait 方法的状态字,当 WIFEXITED(status) 为 0 时表示子进程异常退出,使用 WEXITSTATUS(status) 可以进一步获得子进程的 exit code

写了一个简单的Linux Shell用来下载文件

#!/bin/sh ; i

如何写一个简单的shell

如何写一个简单的shell 看完后我就一直想写一个简单的shell来作为练习,因为有事断断续续的写了好几个月,如今写了差不多来总结一下. 源代码放在了Github: ...

一个简单的linux下设置定时执行shell脚本的示例

很多时候我们有希望服务器定时去运行一个脚本来触发一个操作,比如说定时去备份服务器数据.数据库数据等 不适合人工经常做的一些操作这里简单说下 shell Shell俗称壳,类似于DOS下的command ...

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分 ...

linux设备驱动第三篇:如何写一个简单的字符设备驱动?

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...

如何写一个简单的http服务器

最近几天用C++写了一个简单的HTTP服务器,作为学习网络编程和Linux环境编程的练手项目,这篇文章记录我在写一个HTTP服务器过程中遇到的问题和学习到的知识. 服务器的源代码放在Github. H ...

《Linux内核分析》第三周 构建一个简单的Linux系统MenuOS

[刘蔚然 原创作品转载请注明出处 MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK THREE ...

第三节 构造一个简单的Linux系统MenuOS——20135203齐岳

第三节 构造一个简单的Linux系统MenuOS By 20135203齐岳 Linux内核源代码 arch/ 支持不同cpu的源代码 Documentations/ 文档存储 init/ 内核启动相 ...

&lbrack;转&rsqb;一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...

随机推荐

Hbase安装配置(靠谱亲测)

Hbase是Hadoop生态系统中的NoSql列式数据库.通过Hbase,可以进行数据读写,比较适合Top n场景.Hbase搭建的系统,瓶颈在于硬盘的传输速度.RDBMS一般的瓶颈在于寻道速度. 实 ...

项目解析- JspLibrary - part1

http://rosspc:8080/JspLibrary/ 1. logon界面解析: JS 验证用户名.密码为空

CSS学习进度备忘

书签:“CSS 高级”跳过:另外跳过的内容有待跟进 __________________ 学习资源:W3School. _________________ 跳过的内容:1.“CSS id 选择器”的“ ...

day 2014-04-13

crystal 10:00:40 米多爸爸 11:51:47 很滋润嘛.一般有送股题材的股票都会在送股消息公告之前炒上一阵子,真到了题材兑现就涨不动了,也有些会在除权后走一波填权行情.现在不是牛市,后 ...

LintCode 846&period;多关键字排序

LintCode 846.多关键字排序 描述 给定 n 个学生的学号(从 1 到 n 编号)以及他们的考试成绩,表示为(学号,考试成绩),请将这些学生按考试成绩降序排序,若考试成绩相同,则按学号升序排 ...

MT【96】一道三角恒等变换题

设$a,b,c$是正数,且$(a+b)(b+c)(c+a)=8$,证明不等式:$\frac{a+b+c}{3}≥[\frac{a^3+b^3+c^3}{3}]^{\frac{1}{27}}$ 评:记住 ...

MySQL事务一致性理解

一致性是指数据处于一种语义上的有意义且正确的状态.一致性是对数据可见性的约束,保证在一个事务中的多次操作的数据中间状态对其他事务不可见的.因为这些中间状态,是一个过渡状态,与事务的开始状态和事务的结束 ...

【Go】累加器的测试问题记录

关于GoLang学习过程中的一个问题mark,教程上说两个累加器的地址应该是不一样的,但是实际测试出来结果一样 package main import( "fmt" ) func ...

AES128加密算法完整实现

概述 原本想把自己AES加密算法的整个实现过程给详细复述下来,分享给想学习的同学,也方便自己复习,但后来发现该工作量太大,加上作业太多没有过多的时间去写.所以就想把自己在学习的过程中多遇到的好的文章进 ...

Zabbix应用三&colon;Zabbix监控MySQL

利用Zabbix自带模版监控(Template App MySQL)MySQL服务 一.添加模版: 进入zabbix页面,找到'配置'->'主机'->'模版',点击'链接指示器'后面的'选 ...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值