Linux 用C语言实现简单的shell(2)
不知不觉两周没有发文了,因为“一万美金的福特奖学金答辩”,ACM比赛,网络论文阅读和网络大作业一大堆事把时间冲散了,所以先写一篇博文补上之前一坑。
之前发了一篇关于linux 用C语言实现简单shell的博文,当时因为刚刚接触linux,只是处理了:
1)外部命令
2)pwd,cd,exit内置命令
3)输入输出重定向
并且代码相比较而言是一步一步添加的,代码相对来讲比较丑QAQ,所以在学完管道之后,相信不得不重新写代码才能实现了。
相比较之前的版本我对代码进行了相关的修改:
1)对于shell指令采用结构体存储,方便管道的切割与执行;
2)采用strtok_r对于指令进行了更加合理的切割;
3)增加了多管道处理;
4)处理了一些简单的异常问题,包括文件路径问题,空行问题,指令错误等。
评论中有朋友想让我加入后台运行‘&’功能的,其实基本上没什么区别,只是对于增加了‘&’后台运行的命令主进程不再需要等待子进程结束再操作了,我已经对我的代码进行了一些修改并加上一定的注释。
/*author:Samsons
date:2015.4.28
1)对于shell指令采用结构体存储,方便管道的切割与执行;
2)采用strtok_r对于指令进行了更加合理的切割;
3)增加了多管道处理;
4)处理了一些简单的异常问题,包括文件路径问题,空行问题,指令错误等。
date:2015.4.30
增加了后台处理命令“&”的处理
*/
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/wait.h> #define MAX 100
#define LEN 100 //shell指令单个管道结构体
struct cmd_list{
int argc; //单个管道参数个数
char *argv[MAX];
}; struct cmd_list *cmdv[MAX]; //shell指令
int num;//shell管道个数
int flagdo;//是否为后台处理命令标记 //执行外部命令
void execute(char *argv[])
{
int error;
error=execvp(argv[],argv);
if (error==-) printf("failed!\n");
exit();
} //切分单个管道
void split_cmd(char *line)
{
struct cmd_list * cmd = (struct cmd_list *)malloc(sizeof(struct cmd_list));
cmdv[num++] = cmd;
cmd->argc = ;
char *save;
char *arg = strtok_r(line, " \t", &save);//切分空格
while (arg)
{
cmd->argv[cmd->argc] = arg;
arg = strtok_r(NULL, " \t", &save);
cmd->argc++;
}
cmd->argv[cmd->argc] = NULL;
} //切分管道
void split_pipe(char *line)
{
char *save;
char * cmd = strtok_r(line, "|", &save);
while (cmd) {
split_cmd(cmd);
cmd = strtok_r(NULL, "|", &save);
}
} //执行管道命令
void do_pipe(int index)
{
if (index == num - )
execute(cmdv[index]->argv);
int fd[];
pipe(fd);//创建管道,0读,1写
if (fork() == )
{
dup2(fd[], );
close(fd[]);
close(fd[]);
execute(cmdv[index]->argv);
}
dup2(fd[], );
close(fd[]);
close(fd[]);
do_pipe(index + );
} //执行内部指令
int inner(char *line)
{
char *save,*tmp[MAX];
char t[LEN],p[LEN];
strcpy(t,line);
char *arg = strtok_r(line, " \t", &save);//切分空格
int i=;
while (arg) {
tmp[i] = arg;
i++;//记录命令个数
arg = strtok_r(NULL, " \t", &save);
}
tmp[i] = NULL;
if (strcmp(tmp[i-],"&")==)//判断是否为后台处理命令
{
flagdo=;
i--;
}
if (strcmp(tmp[],"exit")==)//exit
{
exit();
return ;
}
else
if (strcmp(tmp[],"pwd")==)//pwd
{
char buf[LEN];
getcwd(buf,sizeof(buf));//得到当前路径
printf("Current dir is:%s\n",buf);
return ;
}
else
if (strcmp(tmp[],"cd")==)//cd
{
char buf[LEN];
if (chdir(tmp[])>=)
{
getcwd(buf,sizeof(buf));
printf("Current dir is:%s\n",buf);
}
else
{
printf("Error path!\n");
}
return ;
}
else return ;
} //输入重定向
void cat_in(char *q)
{
char t[];
int fd;
if (q[]=='<')
{
strcpy(t,q+);
fd=open(t,O_RDONLY);
cmdv[]->argv[cmdv[]->argc-]=NULL; //默认重定向为参数的最后一个
cmdv[]->argc--;
if (fd==-)
{
printf("file open failed\n");
return;
}
dup2(fd,);
close(fd);
}
} //输出重定向
void cat_out(char *q)
{
char t[];
int fd;
if (q[]=='>')
{
strcpy(t,q+);
cmdv[num-]->argv[cmdv[num-]->argc-]=NULL;
cmdv[num-]->argc--;
fd=open(t,O_CREAT|O_RDWR,); //0666为权限
if (fd==-)
{
printf("file open failed\n");
return;
}
dup2(fd,);
close(fd);
}
} int main()
{
int i,pid;
char buf[LEN],p[LEN];
while ()
{
fgets(buf,LEN,stdin);//读入shell指令
if (buf[]=='\n') continue;
buf[strlen(buf)-]='\0';
strcpy(p,buf);
int inner_flag;
inner_flag=inner(buf);//内置指令执行
if (inner_flag==)
{
pid=fork();//建立新的进程
if (pid==)
{
split_pipe(p);//管道的切割
//如果是后台处理命令将&符号删除
if (strcmp(cmdv[num-]->argv[cmdv[num-]->argc-],"&")==)
{
cmdv[num-]->argc--;
cmdv[num-]->argv[cmdv[num-]->argc]=NULL;
}
//默认输入输出重定向都是最后一个参数,输入时第一个管道,输出是最后一个管道
if (cmdv[]->argv[cmdv[]->argc-]!=NULL)
{
char q[LEN];
strcpy(q,cmdv[]->argv[cmdv[]->argc-]);
cat_in(q);//输入重定向
}
if (cmdv[num-]->argv[cmdv[num-]->argc-]!=NULL)
{
char q[LEN];
strcpy(q,cmdv[num-]->argv[cmdv[num-]->argc-]);
cat_out(q);//输出重定向
}
do_pipe();//执行管道
exit();
}
if (flagdo==)//非后台处理命令主进程才需等待子进程处理
waitpid(pid,NULL,);
}
}
return ;
}
Linux 用C语言实现简单的shell(2)的更多相关文章
- Linux 用C语言实现简单的shell(1)
发一波福利,操作系统的实验内容,大家可以借鉴一下,不过我的代码可能也存在一定的问题. 因为在一开始老师是一节一节课教的,当时并不知道后面还会用输入输出重定向,管道等一系列问题,我的兴趣也不在这个方面也 ...
- linux下c语言实现简单----线程池
这两天刚好看完linux&c这本书的进程线程部分,学长建议可以用c语言实现一个简单的线程池,也是对线程知识的一个回顾与应用.线程的优点有好多,它是"轻量级的进程",所需资源 ...
- 如何在linux下编写一个简单的Shell脚本程序
在了解了linux终端和其搭配的基本Shell(默认为bash)的基础下,我们就可以在终端中用vi/vim编辑器编写一个shell的脚本程序了 Shell既为一种命令解释解释工具,又是一种脚本编程语言 ...
- Linux系统学习笔记之 1 一个简单的shell程序
不看笔记,长时间不用自己都忘了,还是得经常看看笔记啊. 一个简单的shell程序 shell结构 1.#!指定执行脚本的shell 2.#注释行 3.命令和控制结构 创建shell程序的步骤 第一步: ...
- linux 下C语言学习路线
UNIX/Linux下C语言的学习路线.一.工具篇“公欲善其事,必先利其器”.编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习C语言编程常常用到的软件和工 ...
- Unix和Linux下C语言学习指南
转自:http://www.linuxdiyf.com/viewarticle.php?id=174074 Unix和Linux下C语言学习指南 引言 尽管 C 语言问世已近 30 年,但它的魅力仍未 ...
- Linux学习之路(三)Shell脚本初探
本文参考链接:http://www.runoob.com/linux/linux-shell.html 基本说明 Shell脚本(shell script)是一种为shell编写的脚本程序.其中she ...
- Linux内核分析— —构造一个简单的Linux系统MenuOS(20135213林涵锦)
Linux内核分析— —构造一个简单的Linux系统MenuOS 实验内容 Linux内核的启动过程,从start_kernel到init进程启动 使用实验楼的虚拟机打开shell cd LinuxK ...
- 如何写一个简单的shell
如何写一个简单的shell 看完<UNIX环境高级编程>后我就一直想写一个简单的shell来作为练习,因为有事断断续续的写了好几个月,如今写了差不多来总结一下. 源代码放在了Github: ...
随机推荐
- Python学习札记(三) I/O
参考:输入和输出 I/O 1.print()函数 a.调用print()输出字符串有以下两种方式:(1)print('[字符串]') (2)print("[字符串]") b.调用p ...
- LightOJ 1356 Prime Independence(质因数分解+最大独立集+Hopcroft-Carp)
http://lightoj.com/login_main.php?url=volume_showproblem.php?problem=1356 题意: 给出n个数,问最多能选几个数,使得该集合中的 ...
- cocos2d-x入门三 分层设计框架
helloworld就是一个完整的框架,大致分为四个层次如下: 导演-------场景------图层-----精灵 Director-----Scene----Layer----Sprite 导演类 ...
- poj 2255 Tree Recovery 分治
Tree Recovery Description Little Valentine liked playing with binary trees very much. Her favorite g ...
- 解决httpclient请求响应压缩文本乱码问题
最近在调用京东的获取省份接口老是中文乱码,加了utf-8也没有用.最后在httpclient打的日志中有Content-Encoding:gzip信息,最后在请求header里加上: reqHeade ...
- Java之聊天室系统设计一
任务: 先上实现效果图: 登陆界面: index.jsp: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...
- 实例化后的map的默认值
public class map默认值 { public static void main(String[] args) { Map<String, String> resultMap = ...
- HDU-5695-拓扑排序+优先队列
Gym Class Time Limit: 6000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total S ...
- SPOJ 694 && SPOJ 705 (不重复子串个数:后缀数组)
题意 给定一个字符串,求它的所有不重复子串的个数 思路 一个字符串的子串都必然是它的某个后缀的前缀.对于每一个sa[i]后缀,它的起始位置sa[i],那么它最多能得到该后缀长度个子串(n-sa[i]个 ...
- web前端 —— 移动端知识的一些总结
个人在移动端的一些总结归纳,有新的知识点会一直更新 一.css部分 1.meta标签 <meta name="viewport" content="width=de ...