不知不觉两周没有发文了,因为“一万美金的福特奖学金答辩”,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)的更多相关文章

  1. Linux 用C语言实现简单的shell(1)

    发一波福利,操作系统的实验内容,大家可以借鉴一下,不过我的代码可能也存在一定的问题. 因为在一开始老师是一节一节课教的,当时并不知道后面还会用输入输出重定向,管道等一系列问题,我的兴趣也不在这个方面也 ...

  2. linux下c语言实现简单----线程池

    这两天刚好看完linux&c这本书的进程线程部分,学长建议可以用c语言实现一个简单的线程池,也是对线程知识的一个回顾与应用.线程的优点有好多,它是"轻量级的进程",所需资源 ...

  3. 如何在linux下编写一个简单的Shell脚本程序

    在了解了linux终端和其搭配的基本Shell(默认为bash)的基础下,我们就可以在终端中用vi/vim编辑器编写一个shell的脚本程序了 Shell既为一种命令解释解释工具,又是一种脚本编程语言 ...

  4. Linux系统学习笔记之 1 一个简单的shell程序

    不看笔记,长时间不用自己都忘了,还是得经常看看笔记啊. 一个简单的shell程序 shell结构 1.#!指定执行脚本的shell 2.#注释行 3.命令和控制结构 创建shell程序的步骤 第一步: ...

  5. linux 下C语言学习路线

    UNIX/Linux下C语言的学习路线.一.工具篇“公欲善其事,必先利其器”.编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习C语言编程常常用到的软件和工 ...

  6. Unix和Linux下C语言学习指南

    转自:http://www.linuxdiyf.com/viewarticle.php?id=174074 Unix和Linux下C语言学习指南 引言 尽管 C 语言问世已近 30 年,但它的魅力仍未 ...

  7. Linux学习之路(三)Shell脚本初探

    本文参考链接:http://www.runoob.com/linux/linux-shell.html 基本说明 Shell脚本(shell script)是一种为shell编写的脚本程序.其中she ...

  8. Linux内核分析— —构造一个简单的Linux系统MenuOS(20135213林涵锦)

    Linux内核分析— —构造一个简单的Linux系统MenuOS 实验内容 Linux内核的启动过程,从start_kernel到init进程启动 使用实验楼的虚拟机打开shell cd LinuxK ...

  9. 如何写一个简单的shell

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

随机推荐

  1. kotlin 记录(已弃坑)

    kotlin 有些是转载内容 使用nullable值以及空值检测 引用或函数返回值如果可能为null值,则必须显式标记nullable. (在类型后面跟一个问号表示这个对象可能为空,跟两个感叹号表示这 ...

  2. java 枚举的写法

    1.第一种形式: public interface PrealertConstants { enum platformCodeEnum{ CAINIAO(1), CLOSED(-5), OFFLINE ...

  3. linux pgrep命令的使用

    pgrep 是通过程序的名字来查询进程的工具,一般是用来判断程序是否正在运行. 常用参数 -l 列出程序名和进程ID -o 进程起始的ID -n 进程终止的ID $ ps -ef | grep mys ...

  4. 数据库原理及应用-用户接口及SQL查询语言(Query Language)

    2018-02-07 20:41:39 一.DBMS的用户接口 查询语言 访问DBMS的访问工具(GUI) API 相关类库 二.SQL语言 SQL语言可以细分为四种: 1.Data Definiti ...

  5. JavaScript encodeURIComponent()

    ■ 把字符串作为 URI 组件进行编码.JavaScript中有三个可以对字符串编码的函数,分别是: escape,encodeURI,encodeURIComponent,相应3个解码函数:unes ...

  6. git命令速记

    对于不常写代码,有的时候又要提交点代码的人来说,git命令记了又忘,忘了又去花精力记住.有没有一种速记方法,来帮助我们记忆这些玩意呢? 纯属抄袭@_@! 除了几个很基本的命令,复杂一点的,我都要去查, ...

  7. Dev-C++添加代码格式化(format source code)工具Artistic Style

    Dev-C++是一个轻便的C++ IDE开发环境,比起VS2010来轻巧得多.最新的版本是5.4.0是2013年2月14日发布的,下载地址如下: Dev-C++5.4.0 download 它是开源项 ...

  8. 关于BFS和dijkstra(2019.04.20)

    我的BFS板子 struct node{/*略*/};//表示一个状态 std::map<node,bool>vis;//判断每个状态是否已访问过 std::queue<node&g ...

  9. hdu2196树形dp

    有一棵树,找每个节点所能到达的最远距离是多少 dis[u][0]正向最大距离    dis[u][1]正向次大距离     dis[u][2]反向最大距离 先一边dfs求出每个节点的正向最大距离(就是 ...

  10. [嵌入式培训 笔记]-----Vim编辑器使用简介

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 第一讲小结 1. 光标在屏幕文本中的移动既 ...