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中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分 ...
随机推荐
- input/select/textarea标签的readonly效果实现
首先说一下readonly属性的应用场景 表单中,不能编辑对应的文本,但是仍然可以聚焦焦点 在提交表单的时候,该输入项会作为form的一项提交(目的) 这里要说一下disabled和readonly的 ...
- 使用ocr的自动备份还原ocr
1.查看ocr自动备份ocrconfig -showbackup 2.停止所有节点的集群件 3.还原ocr文件ocrconfig -restore <file-name> 4.重启crs, ...
- Anaconda安装与使用
bash Anaconda.....sh命令安装成功 安装位置:/home/amelie/anaconda2 修改路径:vi ~/.bashrc vi编辑器在最后写上:export PATH=/hom ...
- jQuery对select操作小结
//遍历option和添加.移除optionfunction changeShipMethod(shipping){ var len = $("select[@name=ISHIPTYPE] ...
- Latent Dirichlet Allocation 文本分类主题模型
文本提取特征常用的模型有:1.Bag-of-words:最原始的特征集,一个单词/分词就是一个特征.往往一个数据集就会有上万个特征:有一些简单的指标可以帮助筛选掉一些对分类没帮助的词语,例如去停词,计 ...
- PHPwind高级伪静态规则及方法
phpwind iis下伪静态规则[ISAPI_Rewrite]RewriteRule ^(.*)/(.*)-htm-(.*)-(.*)\.html$ $1/$2\.php\?$3=$4Rewrite ...
- java中如何获取昨天的当前日期
在java里,获取昨天的当前日期,可以采用calendar来做,也可以采用date来做:如下: 1.采用calendar来做: Calendar cal=Calendar.getInstance(); ...
- [课程设计]Scrum 2.1 多鱼点餐系统开发进度(下单列表布局)
[课程设计]Scrum 2.1 多鱼点餐系统开发进度(下单列表布局) 1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅到店 ...
- WordCount示例深度学习MapReduce过程(1)
我们都安装完Hadoop之后,按照一些案例先要跑一个WourdCount程序,来测试Hadoop安装是否成功.在终端中用命令创建一个文件夹,简单的向两个文件中各写入一段话,然后运行Hadoop,Wou ...
- python基础(内置函数+文件操作+lambda)
一.内置函数 注:查看详细猛击这里 常用内置函数代码说明: # abs绝对值 # i = abs(-123) # print(i) #返回123,绝对值 # #all,循环参数,如果每个元素为真,那么 ...