UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell
一 分析
要实现一个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)*);
char* prompt = "print your cmd >";
int i; while()
{ i = NextCmd(prompt,cmdLine);
if(i != )
{
return i;
}
else
{
printf("you print a cmd: %s \n",cmdLine);
}
}
free(cmdLine);
return ;
} int NextCmd(char* prompt,char* cmdLine)
{
int i;
printf("%s",prompt);
i = GetsLine(cmdLine);
if(i != )
{
return i;
}
else
{
return ;
}
} int GetsLine(char* result)
{
int word; while()
{
word = getc(stdin);
if(word != '\n')
{
*result = word;
result ++;
}
else
{
*result = '\0';
return ;
}
}
}
输出:
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[];
char* cmdLine = "abc -a -b -c";
int i;
for(i = ;i<;i++)
{
argList[i] = (char*)malloc(sizeof(char)*);
memset(argList[i],'\0',sizeof(char)*);
} CmdToArg(cmdLine,argList); for(i = ;i<;i++)
{
printf("get:%s\n",argList[i]);
} for(i = ;i<;i++)
{
free(argList[i]);
}
return ; } int CmdToArg(char* cmdLine,char* argList[])
{
char aChar;
char* pChar;
int i = ;
pChar = argList[];
while()
{
aChar = *cmdLine;
cmdLine++;
if(aChar == ' ')
{
*pChar = '\0';
i++;
pChar = argList[i];
}
else if(aChar == '\0')
{
*pChar = '\0';
i++;
argList[i] = ;
return ;
}
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[];
int i; arglist[] = "ls";
arglist[] = "-l";
arglist[] = ; i = fork();
if(i==)
{
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/$ ./a.out
I'm the new process.ready for cmd ls
total
-rwxrwxr-x lqx lqx -- : a.out
-rw-rw-r-- lqx lqx -- : execute.c
-rw-rw-r-- lqx lqx -- : psh2.c
-rw-rw-r-- lqx lqx -- : smsh1.c
-rw-rw-r-- lqx lqx -- : smsh.h
-rw-rw-r-- lqx lqx -- : splitline.c
-rw-rw-r-- lqx lqx -- : test.c
-rwxrwxr-x lqx lqx -- : testline
-rw-rw-r-- lqx lqx -- : 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)*);
char* prompt = "print your cmd >";
int i;
char* argList[];
for(i = ;i<;i++)
{
argList[i] = (char*)malloc(sizeof(char)*);
memset(argList[i],'\0',sizeof(char)*);
}
while()
{ i = NextCmd(prompt,cmdLine);
if(i != )
{
return i;
}
else
{
CmdToArg(cmdLine,argList);
i = fork();
if(i==)
{
execvp(argList[],argList);
exit();
}
else
{
wait(NULL);
}
}
}
free(cmdLine);
for(i = ;i<;i++)
{
free(argList[i]);
}
return ;
} int NextCmd(char* prompt,char* cmdLine)
{
int i;
printf("%s",prompt);
i = GetsLine(cmdLine);
if(i != )
{
return i;
}
else
{
return ;
}
} int GetsLine(char* result)
{
int word; while()
{
word = getc(stdin);
if(word != '\n')
{
*result = word;
result ++;
}
else
{
*result = '\0';
return ;
}
}
} int CmdToArg(char* cmdLine,char* argList[])
{
char aChar;
char* pChar;
int i = ;
pChar = argList[];
while()
{
aChar = *cmdLine;
cmdLine++;
if(aChar == ' ')
{
*pChar = '\0';
i++;
pChar = argList[i];
}
else if(aChar == '\0')
{
*pChar = '\0';
i++;
argList[i] = ;
return ;
}
else
{
*pChar = aChar;
pChar++;
}
} }
测试一下:
print your cmd >ls -l
total
-rw-rw-r-- lqx lqx -- : \
-rwxrwxr-x lqx lqx -- : a.out
-rw-rw-r-- lqx lqx -- : execute.c
-rw-rw-r-- lqx lqx -- : psh2.c
-rw-rw-r-- lqx lqx -- : smsh1.c
-rw-rw-r-- lqx lqx -- : smsh.h
-rw-rw-r-- lqx lqx -- : splitline.c
-rw-rw-r-- lqx lqx -- : test.c
-rwxrwxr-x lqx lqx -- : testline
-rw-rw-r-- lqx lqx -- : testline.c
以上,我们实现了一个基本的shell,虽然还有很多不足之处,但是对shell的基本原理和功能都有了一些了解。
UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell的更多相关文章
- Unix/Linux编程实践教程(三:代码、测试)
测试logfilec.c的时候,有个sendto(sock,msg,strlen(msg),0,&addr,addrlen),编译时提示: logfilec.c:30: warning: pa ...
- 学习《Unix/Linux编程实践教程》(1):Unix 系统编程概述
0.目录 1.概念 2.系统资源 3.学习方法 4.从用户的角度来理解 Unix 4.1 登录--运行程序--注销 4.2 目录操作 4.3 文件操作 5.从系统的角度来理解 Unix 5.1 网络桥 ...
- Unix/Linux编程实践教程(二:socket、多线程、进程间通信)
同一接口不同的数据源: 协同进程: fdopen以文件描述符为参数: fopen和popen: 为了实现popen,必须在子进程中调用sh,因为只有shell本身即/bin/sh可以运行任意shell ...
- Unix/Linux编程实践教程(0:文件、终端、信号)
本来只打算读这本书socket等相关内容,但书写得实在好,还是决定把其余的内容都读一下. 阅读联机帮助的一个示例: open系统调用: read系统调用: Unix的time: 上面的printf可以 ...
- Unix/Linux编程实践教程(一:进程、管道)
execvp在程序中启动新程序: 用fork创建新进程: forkdemo2代码: 测试fork的时候参考<Linux权威指南>阅读笔记(3) 使用了patch: [root@local ...
- 学习《Unix/Linux编程实践教程》(2):实现 more
0.目录 1.more 能做什么? 2.more 是如何实现的? 3.实现 more 3.1 more01.c 3.2 more02.c 3.3 more03.c 1.more 能做什么? more ...
- SmartSql使用教程(1)——初探,建立一个简单的CURD接口服务
一.引言 最近SmartSql被正式引入到了NCC,借着这个契机写一个使用教程系列 二.SmartSql简介[摘自官方文档] 1. SmartSql是什么? SmartSql = MyBatis + ...
- linux设备驱动第三篇:如何写一个简单的字符设备驱动?
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...
- linux设备驱动第三篇:写一个简单的字符设备驱动
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分 ...
随机推荐
- Android 高德地图No implementation found for long com.autonavi.amap.mapcore.MapCore
此篇博客最后更新时间写自2016.5.18.当下高德地图jar版本为3.3.1. 使用高德地图碰到此问题,纠结许久(接近4个多小时). 记录在此,希望遇到相同问题的读者可以有所借鉴. 错误截图: 导致 ...
- jquery判断页面是否滑动到最底部
// 滚动到底部,向下的箭头消失 var $down = $('.down'); var $window = $(window); var $document = $(document); $wind ...
- 安装vim
命令安装vim sudo apt-get install vim
- android studio 引入第三方类库jar包
第三方类库jar包 这就简单多了,直接将jar包拷贝到app/libs下,然后在app下的build.gradle中添加此jar的依赖.如下: dependencies { compile 'com. ...
- JSP-10-JSTL标准标签库
JSTL (jsp 标准标签库) 包含用于编写和开发JSP页面的一组标准标签,它可为用户提供一个无脚本的环境. JSTL 提供了4个主要的标签库: 核心标签库.国际化(I18N)与格式化标签库.XML ...
- Spark分析笔记
前言 第一章 Spark简介 本章将对Spark做一个介绍,以及它的一些基本概念 Spark是什么? Spark生态系统BDAS Spark架构 Spark分布式与单机多核架构的异同 Spark的企业 ...
- Apple开发者账号简介
苹果对开发者主要分为3类:个人.组织(公司.企业).教育机构.即: 1.个人(Individual) 2.组织(Organizations) 组织类又分为2个小类: (1)公司(Company) (2 ...
- Dynamics AX 2012 R2 RemoteApp导出项目报错
今天,Reinhard使用RemoteApp的方式登陆AX开发环境,对项目文件进行修改后,习惯性地将项目导出到Reinhard的电脑上,做个备份.但是导出时弹出错误提示框,报以下错误: ...
- NaN
not a number 全称, 任何数/0 js会出现NaN alert(NaN==NaN); // false isNaN(NaN); // true alert(isNaN(10)); // f ...
- codeforces VK cup 2016-round 1 D.Bear and Contribution
题意大概就是有n个数字,要使至少有k个相同,可以花费b使一个数+5,可以花费c使一个数+1,求最小花费. 要对齐的数肯定是在[v,v+4]之间,所以分别枚举模为0~4的情况就可以了. 排序一下,然后化 ...