实现一个自己的shell程序,这个程序有这些功能:解释执行命令,支持输入输出重定向,支持管道,后台运行

程序。当运行该程序后,它支持以下的命令格式:

1.单个命令,如:ls。2.带l到多个参数的命令,如ls -l。3.带一个输出重定向的命令。4.带一个输入重定向的

命令。5.带一个管道的命令。6.后台运行符&可加在各个命令的最后面。7.输入exit或logout退出myshell。

错误处理:1.输入错误的命令格式报错。2.输入不存在的命令报错。

程序主函数的流程图如下:

程序的各个函数的功能及说明:

(1)void print_prompt():该函数只是简单地打印myshell的提示符,即“myshell$”。

(2)void get_input(char *buf):获得一条用户输入的待执行命令,参数buf用于存放输入的命令。如果输入的

命令过长(大于256个字符),则终止程序。输入的命令以换行符'\n'作为结束标志。

(3)void explain_input(char *buf,int *argcount,char arglist[100][256]):解析buf中存放的命令,把每个

选项存放在arglist中。

(4)do_cmd(int argcount,char arglist[100][256]):执行arglist中存放的命令,arglist为待执行命令的参数

的个数。

(5)int find_command(char *command):功能是分别在当前目录下、/bin、/usr/bin目录下查找命令的可执行程

序。

下面是该程序的源代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#define normal 0 //一般的命令
#define out_redirect 1 //输出重定向
#define in_redirect 2 //输入重定向
#define have_pipe 3 //命令中有管道 void print_prompt(); //打印提示符
void get_input(char*); //得到输入的命令
void explain_input(char*,int*,char a[][]); //对输入命令进行解析
void do_cmd(int,char a[][]); //执行命令
int find_command(char*); //查找命令中的可执行程序 int main(int argc,char *argv[])
{
int i;
int argcount= ;
char arglist[][];
char *buf= NULL;
char **arg= NULL;
buf= (char*)malloc();
if(buf == NULL)
{
perror("malloc failed");
exit(-);
}
while()
{
memset(buf,,); //将buf所指向的空间清零
print_prompt();
get_input(buf);
//若输入的命令为exit或logout则退出本程序
if(strcmp(buf,"exit\n")== ||strcmp(buf,"logout\n")== )
break;
for(i= ;i< ;i++)
{
arglist[i][]= '\0';
}
argcount= ;
explain_input(buf,&argcount,arglist);
do_cmd(argcount,arglist);
}
if(buf!= NULL)
{
free(buf);
buf= NULL;
}
return ;
} void print_prompt()
{
printf("myshell$ ");
} //获取用户输入
void get_input(char *buf)
{
int len= ;
int ch;
ch= getchar();
while(len< &&ch!= '\n')
{
buf[len++]= ch;
ch= getchar();
}
if(len== )
{
printf("command is too long\n");
exit(-); //输入的命令过长则退出程序
}
buf[len]= '\n';
len++;
buf[len]= '\0';
} /*解析buf中的命令,将结果存入arglist中,命令以回车符号\n结束,如输入命令为
*"ls -l /tmp",则arglist[0]、arglist[1]、arglist[2]分别为ls、-l、/tmp */
void explain_input(char *buf,int *argcount,char arglist[][])
{
char *p= buf;
char *q= buf;
int number= ;
while()
{
if(p[]== '\n')
break;
if(p[]== ' ')
p++;
else
{
q = p;
number= ;
while((q[]!= ' ')&&(q[]!= '\n'))
{
number++;
q++;
}
strncpy(arglist[*argcount],p,number+ );
arglist[*argcount][number]= '\0';
*argcount= *argcount+ ;
p = q;
}
}
} void do_cmd(int argcount,char arglist[][])
{
int flag= ;
int how= ; //用于指示命令中是否含有>、<、|
int background= ; //标识命令中是否有后台运行标识符&
int status;
int i;
int fd;
char* arg[argcount+ ];
char* argnext[argcount+ ];
char* file;
pid_t pid;
//将命令取出
for(i= ;i< argcount;i++)
{
arg[i]= (char*)arglist[i];
}
arg[argcount]= NULL;
//查看命令行是否有后台运行符
for(i= ;i< argcount;i++)
{
if(strncmp(arg[i],"&",)== )
{
if(i== argcount-)
{
background= ;
arg[argcount-]= NULL;
break;
}
else
{
printf("wrong command\n");
return;
}
}
}
for(i= ;arg[i]!= NULL;i++)
{
if(strcmp(arg[i],">")== )
{
flag++;
how= out_redirect;
if(arg[i+ ]== NULL)
flag++;
}
if(strcmp(arg[i],"<")== )
{
flag++;
how= in_redirect;
if(i== )
flag++;
}
if(strcmp(arg[i],"|")== )
{
flag++;
how= have_pipe;
if(arg[i+ ]== NULL)
flag++;
if(i== )
flag++;
}
}
/* flag大于1,说明命令中含有多个>,<,|符号,本程序是不支持这样的命令的,或者命令格式不对,如"ls -l /tmp >" */
if(flag> )
{
printf("wrong command\n");
return;
}
if(how== out_redirect)
{
//命令只含有一个输出重定向符号
for(i= ;arg[i]!= NULL;i++)
{
if(strcmp(arg[i],">")== )
{
file= arg[i+ ];
arg[i]= NULL;
}
}
}
if(how== in_redirect)
{
//命令中只含有一个输入重定向符号
for(i= ;arg[i]!= NULL;i++)
{
if(strcmp(arg[i],"<")== )
{
file= arg[i+ ];
arg[i]= NULL;
}
}
}
if(how== have_pipe)
{
/*命令只含有一个管道符号|,把管道符号后面的部分存入argnext中,管道后面的部分是一个可执行的shell命令 */
for(i= ;arg[i]!= NULL;i++)
{
if(strcmp(arg[i],"|")== )
{
arg[i]= NULL;
int j;
for(j= i+ ;arg[j]!= NULL;j++)
{
argnext[j-i-]= arg[j];
}
argnext[j-i-]= arg[j];
break;
}
}
}
if((pid= fork())< )
{
printf("fork error\n");
return;
}
switch(how)
{
case :
/*pid为0说明是子进程,在子进程中执行输入的命令,输入的命令不含>、<、和| */
if(pid== )
{
if(!(find_command(arg[])))
{
printf("%s : command not found\n",arg[]);
exit();
}
execvp(arg[],arg);
exit();
}
break;
case :
//输入的命令中含有输出重定向符>
if(pid== )
{
if(!(find_command(arg[])))
{
printf("%s : command not found\n",arg[]);
exit();
}
fd= open(file,O_RDWR|O_CREAT|O_TRUNC,);
dup2(fd,);
execvp(arg[],arg);
exit();
}
break;
case :
//输入的命令中含有输入重定向符<
if(pid== )
{
if(!(find_command(arg[])))
{
printf("%s : command not found\n",arg[]);
exit();
}
fd= open(file,O_RDONLY);
dup2(fd,);
execvp(arg[],arg);
exit();
}
break;
case :
//输入的命令中含有管道符|
if(pid== )
{
int pid2;
int status2;
int fd2;
if((pid2= fork())< )
{
printf("fork2 error\n");
return;
}
else if(pid2== )
{
if(!(find_command(arg[])))
{
printf("%s : command not found\n",arg[]);
exit();
}
fd2= open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,);
dup2(fd2,);
execvp(arg[],arg);
exit();
}
if(waitpid(pid2,&status2,)== -)
printf("wait for child process error\n");
if(!(find_command(argnext[])))
{
printf("%s : command not found\n",argnext[]);
exit();
}
fd2= open("/tmp/youdonotknowfile",O_RDONLY);
dup2(fd2,);
execvp(argnext[],argnext);
if(remove("/tmp/youdonotknowfile"))
printf("remove error\n");
exit();
}
break;
default:
break;
}
//若命令中有&,表示后台执行,父进程直接返回,不等待子进程结束
if(background== )
{
printf("[process id %d]\n",pid);
return;
}
//父进程等待子进程结束
if(waitpid(pid,&status,)== -)
printf("wait for child process error\n");
} //查找命令中的可执行程序
int find_command(char *command)
{
DIR *dp;
struct dirent *dirp;
char *path[]= {"./","/bin","/usr/bin",NULL};
//使当前目录下的程序可以运行,如命令"./fork"可以被正确解释和执行
if(strncmp(command,"./",)== )
command= command+ ;
//分别在当前目录、/bin和/usr/bin目录查找要执行的程序
int i= ;
while(path[i]!= NULL)
{
if((dp= opendir(path[i]))== NULL)
printf("can not open /bin \n");
while((dirp= readdir(dp))!= NULL)
{
if(strcmp(dirp->d_name,command)== )
{
closedir(dp);
return ;
}
}
closedir(dp);
i++;
}
return ;
}

实现一个shell程序的更多相关文章

  1. Linux Shell 之 我的第一个Shell程序

      这里我首先会介绍一个Shell是什么,再介绍我的第一个Shell程序和从中总结的经验. 一.Shell是什么 在说我的这个Shell程序之前,还是先跟大家说说什么是Shell吧,相信Shell这个 ...

  2. shell脚本,通过一个shell程序计算n的阶乘。

    [root@localhost ~]# cat jiechen.sh #!/bin/bash #设计一个shell程序计算n的阶乘,要求: #.从命令行接收参数n; #.在程序开始后立即判断n的合法性 ...

  3. 一个shell程序

    使用vi写一个shell程序 touch cdlog  echo "cd /app/mycrm/log" >> cdlog  chmod +x cdlog   执行: ...

  4. Linux Shell编程(2)——第一个shell程序

    在最简单的情况下,脚本程序不过是存储在一个文件里的系统命令列表.这至少让你执行它 时不必重新按顺序键入相同功能的命令序列.一个清空/var/log目录下的日志文件的脚本 # Cleanup # 必须以 ...

  5. 第一个shell程序

    前言:我为什么又来学习shell呢?因为这个轻量级的编程小脚本语言能够帮我处理一些基于linux的复杂手工工作.真是一言难尽,学会一门又来一门!! 看了2天这个教程,试着写了一个小脚本,没啥技术含量, ...

  6. Linux shell程序一

    设计一个Shell程序,在/$HONE/test目录下建立50个目录,即user1-user50, 并设置每个目录的权限,其中其他用户的权限为:读:文件所有者的权限为: 读.写.执行:文件所有者所在组 ...

  7. 学习Shell脚本编程(第4期)_在Shell程序中的使用变量

    变量的赋值 变量的访问 变量的输入 4.1 变量的赋值     在Shell编程中,所有的变量名都由字符串组成,并且不需要对变量进行声明.要赋值给一个变量,其格式如下: 变量名=值  注意: 等号(= ...

  8. 学习Shell脚本编程(第3期)_在Shell程序中使用的参数

    位置参数 内部参数 如同ls命令可以接受目录等作为它的参数一样,在Shell编程时同样可以使用参数.Shell程序中的参数分为位置参数和内部参数等. 3.1 位置参数 由系统提供的参数称为位置参数.位 ...

  9. 学习Shell脚本编程(第2期)_编写修改权限及执行Shell程序的步骤

    编写Shell程序 执行Shell程序 Shell程序有很多类似C语言和其他程序设计语言的特征,但是又没有程序语言那样复杂.Shell程序是指放在一个文件中的一系列Linux命令和实用程序.在执行的时 ...

随机推荐

  1. 解决 spring cloud 项目的 com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect 错误信息

    在项目中引入:引入hystrix依赖,如下 <dependency> <groupId>org.springframework.cloud</groupId> &l ...

  2. PHP and laravel知识点小小积累

    function () use ($x, &$y){} 自从PHP5.3开始有了closure/匿名函数的概念,在这里的use关键词的作用是允许匿名函数capture到父函数scope 内存在 ...

  3. jquery mobile开发中常见的问题(转载)

    1页面缩放显示问题 问题描述: 页面似乎被缩小了,屏幕太宽了. 处理方法: 在head标签内加入: <meta name="viewport" content="w ...

  4. 【NLP_Stanford课堂】语言模型4

    平滑方法: 1. Add-1 smoothing 2. Add-k smoothing 设m=1/V,则有 从而每一项可以跟词汇表的大小相关 3. Unigram prior smoothing 将上 ...

  5. windows下安装配置RabbitMQ

    安装部署 1.当前环境以及参考资料出处 部署环境:windows server 2008 r2 enterprise 官方安装部署文档:http://www.rabbitmq.com/install- ...

  6. Topshelf Configuration z

    Topshelf Configuration While the Quickstart gives you enough to get going, there are many more featu ...

  7. Dz7.2 从获取uc key到getshell

    0x01下午在群里看到的 问下朋友大概 然后用不生不熟的sqlmap在那跑–表能跑的出 列就GG了 然后没辙–晚上跑各大论坛逛了遍果然大神多 就慢慢的研究下了下 看了pt0n大牛的分析文 真心感叹这洞 ...

  8. C#图解教程读书笔记(第5章 方法)

    类型推断和var关键字 从C#3.0开始,可以在变量声明的开始部分的的位置使用新的关键字var. Var关键字并不是某种特别类型的符号.它只是句法上的速记,表示任何可以从初始化的右边推断出的类型. V ...

  9. dynamic_cast动态转换

    我们都知道dynamic_cast会在运行时进行类型检查,比较安全,static_cast静态转换,不安全 dynamic_cast转换的类型必须是个类,且这个类中必须有虚函数,为什么呢? 虚函数对于 ...

  10. 对HandlerExecutionChain类的理解分析

    HandlerExecutionChain类比较简单,好理解. ==================================================================== ...