设为首页 收藏本站
查看: 435|回复: 0

[经验分享] Linux 用C语言实现简单的shell(2)

[复制链接]

尚未签到

发表于 2015-10-27 09:06:46 | 显示全部楼层 |阅读模式
  不知不觉两周没有发文了,因为“一万美金的福特奖学金答辩”,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[0],argv);
if (error==-1)  printf("failed!\n");
exit(1);
}
//切分单个管道
void split_cmd(char *line)
{
struct cmd_list * cmd = (struct cmd_list *)malloc(sizeof(struct cmd_list));
cmdv[num++] = cmd;
cmd->argc = 0;
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 - 1)
execute(cmdv[index]->argv);
int fd[2];
pipe(fd);//创建管道,0读,1写
if (fork() == 0)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
execute(cmdv[index]->argv);
}
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
do_pipe(index + 1);
}
//执行内部指令
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=0;
while (arg) {
tmp = arg;
i++;//记录命令个数
arg = strtok_r(NULL, " \t", &save);
}
tmp = NULL;
if (strcmp(tmp[i-1],"&")==0)//判断是否为后台处理命令
    {
flagdo=1;
i--;
}
if (strcmp(tmp[0],"exit")==0)//exit
    {
exit(0);
return 1;
}
else
if (strcmp(tmp[0],"pwd")==0)//pwd
    {
char buf[LEN];
getcwd(buf,sizeof(buf));//得到当前路径
printf("Current dir is:%s\n",buf);
return 1;
}
else
if (strcmp(tmp[0],"cd")==0)//cd
    {
char buf[LEN];
if (chdir(tmp[1])>=0)
{
getcwd(buf,sizeof(buf));
printf("Current dir is:%s\n",buf);
}
else
{
printf("Error path!\n");
}
return 1;
}
else return 0;
}
//输入重定向
void cat_in(char *q)
{
char t[30];
int fd;
if (q[0]=='<')
{
strcpy(t,q+1);
fd=open(t,O_RDONLY);
cmdv[0]->argv[cmdv[0]->argc-1]=NULL;  //默认重定向为参数的最后一个
cmdv[0]->argc--;
if (fd==-1)
{
printf("file open failed\n");
return;
}
dup2(fd,0);
close(fd);
}
}
//输出重定向
void cat_out(char *q)
{
char t[30];
int fd;
if (q[0]=='>')
{
strcpy(t,q+1);
cmdv[num-1]->argv[cmdv[num-1]->argc-1]=NULL;
cmdv[num-1]->argc--;
fd=open(t,O_CREAT|O_RDWR,0666); //0666为权限
if (fd==-1)
{
printf("file open failed\n");
return;
}
dup2(fd,1);
close(fd);
}
}
int main()
{
int i,pid;
char buf[LEN],p[LEN];
while (1)
{
fgets(buf,LEN,stdin);//读入shell指令
if (buf[0]=='\n') continue;
buf[strlen(buf)-1]='\0';
strcpy(p,buf);
int inner_flag;
inner_flag=inner(buf);//内置指令执行
if (inner_flag==0)
{
pid=fork();//建立新的进程
if (pid==0)
{
split_pipe(p);//管道的切割
//如果是后台处理命令将&符号删除
if (strcmp(cmdv[num-1]->argv[cmdv[num-1]->argc-1],"&")==0)
{
cmdv[num-1]->argc--;
cmdv[num-1]->argv[cmdv[num-1]->argc]=NULL;
}
//默认输入输出重定向都是最后一个参数,输入时第一个管道,输出是最后一个管道
if (cmdv[0]->argv[cmdv[0]->argc-1]!=NULL)
{
char q[LEN];
strcpy(q,cmdv[0]->argv[cmdv[0]->argc-1]);
cat_in(q);//输入重定向
                }
if (cmdv[num-1]->argv[cmdv[num-1]->argc-1]!=NULL)
{
char q[LEN];
strcpy(q,cmdv[num-1]->argv[cmdv[num-1]->argc-1]);
cat_out(q);//输出重定向
                }
do_pipe(0);//执行管道
exit(0);
}
if (flagdo==0)//非后台处理命令主进程才需等待子进程处理
waitpid(pid,NULL,0);
}
}
return 0;
}
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-131223-1-1.html 上篇帖子: linux 安装JDK 下篇帖子: hibernate存取中文出现乱码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表