一 分析

  要实现一个shell,需包含3个步骤
  1)读入指令
  2)指令解析
  3)执行指令

1 从键盘读入指令

  从键盘读入指令的几个要点:

  1)调用getc函数等待并获取用户键盘输入。
  2)每一行命令的结束符为'\n',getsline函数就是通过这个结束符来判断用户是否完成指令的输入。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{char* cmdLine = (char*)malloc(sizeof(char)*100);char* prompt = "print your cmd >";int i;while(1){i = NextCmd(prompt,cmdLine);if(i != 0){return i;}else{printf("you print a cmd: %s \n",cmdLine);}    }free(cmdLine);return 0;
}int NextCmd(char* prompt,char* cmdLine)
{int i;printf("%s",prompt);i = GetsLine(cmdLine);if(i != 0){return i;}else{return 0;}
}int GetsLine(char* result)
{int word;while(1){word = getc(stdin);if(word != '\n'){*result = word;result ++;}else{*result = '\0';return 0;}}
}

输出:

print your cmd >asd
you print a cmd: asd
print your cmd >a
you print a cmd: a
print your cmd >

  在上面的代码中我们从键盘上获得用户的指令输入,然后再直接打印出来。在GetsLine()函数执行getc(),等待用户输入并开始记录,直到用户输入回车后返回,返回前添加'\0'字符表示整条指令结束。在NextCmd()中输出提示符“print your cmd >”,执行GetsLine获得用户输入的整条指令并返回。最后在主函数中打印用户的输入。

2 指令解析

  指令解析的几个要点:

  1)支持的指令格式【指令】 【参数1】 【参数2】。。。【参数n】
  2)指令与参数间,参数与参数间均以空格隔开,一条完整的指令以'\0'结尾。
  3)因为使用函数execvp来执行指令(在后面会讲到),所以需要将解析的指令存入指针数组char* argList[]中,结尾处为NULL。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>int main()
{char* argList[20];char* cmdLine = "abc -a -b -c";int i;for(i = 0;i<20;i++){argList[i] = (char*)malloc(sizeof(char)*100);memset(argList[i],'\0',sizeof(char)*100);}CmdToArg(cmdLine,argList);for(i = 0;i<20;i++){printf("get:%s\n",argList[i]);}for(i = 0;i<20;i++){free(argList[i]);}    return 0;}int CmdToArg(char* cmdLine,char* argList[20])
{char aChar;char* pChar;int i = 0;pChar = argList[0];while(1){aChar = *cmdLine;cmdLine++;if(aChar == ' '){*pChar = '\0';i++;pChar = argList[i];}else if(aChar == '\0'){*pChar = '\0';i++;argList[i] = 0; return 0;}else{*pChar = aChar;pChar++;}}}

  上述代码中,使用CmdToArg()函数,将cmdLine中的字符以空格符为间断符号解析,并存入一个argList中,且在argList的末尾处添加‘0’字符,表明一行指令的结束。

3 执行指令

  我们使用execvp()函数来启动另一个程序,这里有几点需要注意:

  1)新的程序会覆盖原程序,导致新程序结束时原本的shell也结束了,所以需要使用fork()函数来新建一个进程来打开新的程序。
  2)fork()函数的返回值可以判断当前进程是父进程还是子进程。
  3)在父进程中使用wait()函数来等待新进程结束

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>int main()
{char* arglist[3];int i;arglist[0] = "ls";arglist[1] = "-l";arglist[2] = 0;    i = fork();if(i==0){printf("I'm the new process.ready for cmd ls\n");execvp("ls",arglist);}else{wait(NULL);printf("I'm the old process\n");}
}    

  在上面的代码中,我们使用fork()函数创建了一个新的进程,并在新进程中使用execvp()函数来启动新的程序,并在父进程中使用wait()函数来等待子进程结束。输出如下:

lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ ./a.out
I'm the new process.ready for cmd ls
total 44
-rwxrwxr-x 1 lqx lqx 7268 2013-07-02 10:33 a.out
-rw-rw-r-- 1 lqx lqx  497 2013-04-25 15:12 execute.c
-rw-rw-r-- 1 lqx lqx  482 2013-04-23 15:54 psh2.c
-rw-rw-r-- 1 lqx lqx  584 2013-04-25 15:10 smsh1.c
-rw-rw-r-- 1 lqx lqx  202 2013-04-25 15:14 smsh.h
-rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c
-rw-rw-r-- 1 lqx lqx  438 2013-07-02 10:33 test.c
-rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline
-rw-rw-r-- 1 lqx lqx  436 2013-05-10 14:46 testline.c
I'm the old process

4 整合

  在前面,我们分别实现了:
  1)从用户终端获得用户的指令输入
  2)将一行指令解析为指定格式
  3)创建子进程来执行用户的指令

  现在就将以上3各部分整合到一起,实现一个基本的shell。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>int main()
{char* cmdLine = (char*)malloc(sizeof(char)*100);char* prompt = "print your cmd >";int i;char* argList[20];for(i = 0;i<20;i++){argList[i] = (char*)malloc(sizeof(char)*100);memset(argList[i],'\0',sizeof(char)*100);}while(1){i = NextCmd(prompt,cmdLine);if(i != 0){return i;}else{CmdToArg(cmdLine,argList);i = fork();if(i==0){execvp(argList[0],argList);exit(0);}else{wait(NULL);}}    }free(cmdLine);for(i = 0;i<20;i++){free(argList[i]);}  return 0;
}int NextCmd(char* prompt,char* cmdLine)
{int i;printf("%s",prompt);i = GetsLine(cmdLine);if(i != 0){return i;}else{return 0;}
}int GetsLine(char* result)
{int word;while(1){word = getc(stdin);if(word != '\n'){*result = word;result ++;}else{*result = '\0';return 0;}}
}int CmdToArg(char* cmdLine,char* argList[20])
{char aChar;char* pChar;int i = 0;pChar = argList[0];while(1){aChar = *cmdLine;cmdLine++;if(aChar == ' '){*pChar = '\0';i++;pChar = argList[i];}else if(aChar == '\0'){*pChar = '\0';i++;argList[i] = 0; return 0;}else{*pChar = aChar;pChar++;}}}

  测试一下:

print your cmd >ls -l
total 48
-rw-rw-r-- 1 lqx lqx 1863 2013-07-02 10:52 \
-rwxrwxr-x 1 lqx lqx 7491 2013-07-02 11:10 a.out
-rw-rw-r-- 1 lqx lqx  497 2013-04-25 15:12 execute.c
-rw-rw-r-- 1 lqx lqx  482 2013-04-23 15:54 psh2.c
-rw-rw-r-- 1 lqx lqx  584 2013-04-25 15:10 smsh1.c
-rw-rw-r-- 1 lqx lqx  202 2013-04-25 15:14 smsh.h
-rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c
-rw-rw-r-- 1 lqx lqx 1842 2013-07-02 11:10 test.c
-rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline
-rw-rw-r-- 1 lqx lqx  436 2013-05-10 14:46 testline.c

  以上,我们实现了一个基本的shell,虽然还有很多不足之处,但是对shell的基本原理和功能都有了一些了解。

转载于:https://www.cnblogs.com/cation/p/3045240.html

UNIX-LINUX编程实践教程-第八章-实例代码注解-写一个简单的shell相关推荐

  1. 《Unix/linux编程实践教程》------重定向程序的I/O

    <Unix/linux编程实践教程>书中举例命令more的用法: $more filename $command | more $more < filename 用法1直接显示fil ...

  2. Unix/Linux编程实践教程–书评

    花了两个月的时间把这本书读完了,完成了一部分的课后习题. 总的来说,这是一本挺好的Unix\Linux编程的入门书(虽然书中的小错误一大堆),书的开始部分简要介绍了Unix系统编程,讲述了如何使用男人 ...

  3. stty详解-Unix/Linux编程实践教程第五章 学习stty

    读书笔记-第五章 连接控制 学习stty 先放上思维导图 为设备编程&设备就像文件 前面所学的知识都是基于文件的,而在unix/linux系统中,所有的设备(打印机,终端,话筒,摄像头等等)也 ...

  4. 进程和程序:编写shell——《Unix/Linux编程实践教程》读书笔记(第8章)

    2019独角兽企业重金招聘Python工程师标准>>> 1.Unix shell的功能 shell是一个管理进程和运行程序的程序.所有常用的shell都有3个主要功能: (1)运行程 ...

  5. linux pipe函数 重定向,I/O重定向和管道——《Unix/Linux编程实践教程》读书笔记(第10章)...

    1.I/O重定向的概念与原因 及 标准输入.输出的标准错误的定义 所以的Unix I/O重定向都基于标准数据流的原理.三个数据了分别如下: 1)标准输入--需要处理的数据流 2)标准输出--结果数据流 ...

  6. linux编程课后作业,Unix/Linux 编程实践教程第三章习题

    tips: 答案仅供参考,不保证正确* 3.1 在我的 deepin 上,d_name 的长度是 256,有的系统定义成 1 是为了节省空间,使用时可以通过 malloc() 动态的分配空间,比如: ...

  7. unix/linux编程实践,Unix/Linux编程实践教程–cp在OS X的实现

    环境:OS X10.12.4 书中的代码对于OS X是适用的.值得提一嘴的是,其实OS X的open是可以在文件不存在的时候自动创建的,man 2 open时,就可以看到这个说明. The oflag ...

  8. mvc 实例 php,来写一个简单的PHP MVC结构

    MVC结构,其实就是三个Model,Contraller,View单词的简称,Model,主要任务就是把数据库或者其他文件系统的数据按照我们需要的方式读取出来.View,主要负责页面的,把数据以htm ...

  9. Linux:写一个简单的shell求和脚本并运行

    步骤 1.创建空脚本 touch sum.sh 2.编辑脚本文件 vi sum.sh 3.向脚本中添加代码 #!/bin/bash i=0;sum=0 while [ $i -le 10 ] do l ...

最新文章

  1. 《少有人走的路:心智成熟的旅程》--[美]M·斯科特·派克
  2. python绘制饼图双层_Python入门进阶:Python绘制饼图到Microsoft Excel
  3. 介绍一个对陌生程序快速进行性能瓶颈分析的技巧
  4. 【OpenCV】边缘检测
  5. 蓝桥杯 ADV-62算法提高 夺宝奇兵(动态规划)
  6. Perl脚本语言学习1:
  7. 数模算法:模糊综合评价模型
  8. 黄永成think php rest,Rest控制器
  9. 使用三边定位算法进行室内定位
  10. Word:开机即启动Word程序(转)
  11. WPS word编辑图表数据的正确姿态
  12. mysql oob_mysql 读写文件特性和OOB注入
  13. 微信小程序实现九宫格切图,保存功能!
  14. 图形学基础|球谐光照(Spherical Harmonics Lighting)
  15. 基于python的json脚本解析
  16. NOI OJ 1.3 15:苹果和虫子 C语言
  17. win7系统wifi没有网络连接到服务器,Win7无线Wifi无法连接怎么办?win7系统Wifi无法连接的三种故障原因及解决方法...
  18. Eclipse美观化代码
  19. 苹果宣布推出全新辅助功能 Apple Watch支持辅助触控
  20. [附源码]Python计算机毕业设计餐厅订餐系统

热门文章

  1. 微信小程序格创校园跑腿小程序v1.1.64+前端
  2. 夏日清凉小风扇网站源码 抖音引流神器
  3. 失心漂亮的个人html引导页面源码
  4. 备份自己常用的VS2010设置
  5. Linux Shell高级技巧(二)
  6. VScode配置java开发环境
  7. module_param的使用
  8. 除了ssh外也可以开启telnet服务连接服务器
  9. Linux中的内存段(BSS、数据段、代码段、堆、栈)
  10. Linux文件(目录)命名规则