实现一个自己的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. android popupwindow位置显示

    1.在控件的上方: private void showPopUp(View v) { LinearLayout layout = new LinearLayout(this); layout.setB ...

  2. silverlight generic.xaml 包含中文 编译错误的问题

    发现我在一个dll工程里面新建一个xaml文件起名成generic.xaml 如果这个xaml里面存在中文则会编译错误,发现这样建立的xaml使用的是gb2312编码 果断文件-另存为-编码另存为 u ...

  3. java面试题之----IO与NIO的区别

    JAVA NIO vs IO 当我们学习了Java NIO和IO后,我们很快就会思考一个问题: 什么时候应该使用IO,什么时候我应该使用NIO 在下文中我会尝试用例子阐述java NIO 和IO的区别 ...

  4. C# 使用 Invoke 实现函数的白盒 UT 测试

    公有方法可以直接调用,但是一些非公开的方法,在覆盖率测试的时候也需要覆盖,可以使用 Invoke 来调用. 调用方法如下,其中 this 可以改为被调用的方法所属的类名,通过 BindingFlags ...

  5. U盘中毒了?教你如何删除System Volume Information这个顽固文件夹

    不得不说cmd命令很好用呢.最近我的U盘中毒了,格式化都删除不了System Volume Information这个顽固的文件夹,真心伤不起哇!还好现在解决了问题.看来以后得好好对待U盘,不能乱用了 ...

  6. laravel 使用EasyWechat 3分钟完成微信支付(以APP支付为例)

    上一篇写了支付宝支付,然后这段时间我又把微信支付给接上了,作为萌新的我还是很有成就感的,哈哈~~好了,该写正事了. 第一步:创建应用及配配置  首先到微信的官方平台注册应用https://pay.we ...

  7. css3 box-shadow属性 鼠标移动添加阴影效果

    text-shadow是给文本添加阴影效果,box-shadow是给元素块添加周边阴影效果. 基本语法:{box-shadow:[inset] x-offset  y-offset  blur-rad ...

  8. easyui学习笔记3—在展开行内的增删改操作

    这一篇介绍在一个展开行内进行的增删操作,如果一行很长有很多的数据,在一个展开行内进行编辑将更加方便. 1.先看引用的资源 <link rel="stylesheet" hre ...

  9. Spring AOP 的切点切在Controller上没有起作用的问题。

    做下笔记,以防忘记. 在Spring MVC 中使用面向切面编程,感觉所有的配置.切面.切点的编写都没有写错,但是当切点切在@Controller注解的类的方法上时就不起作用.当切点切在的其他的非@C ...

  10. 在一个jsp页面中引用另一个jsp文件的路径的问题

    为了提高代码的重用性,经常需要在一个jsp页面中引用另一个jsp文件的代码.关于引用时的路径的问题.下面只介绍动态引用的,我觉得静态引用的应该也一样. 相对路径: 一.在同一个文件夹下,a.jsp中引 ...