实现一个shell程序
实现一个自己的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程序的更多相关文章
- Linux Shell 之 我的第一个Shell程序
这里我首先会介绍一个Shell是什么,再介绍我的第一个Shell程序和从中总结的经验. 一.Shell是什么 在说我的这个Shell程序之前,还是先跟大家说说什么是Shell吧,相信Shell这个 ...
- shell脚本,通过一个shell程序计算n的阶乘。
[root@localhost ~]# cat jiechen.sh #!/bin/bash #设计一个shell程序计算n的阶乘,要求: #.从命令行接收参数n; #.在程序开始后立即判断n的合法性 ...
- 一个shell程序
使用vi写一个shell程序 touch cdlog echo "cd /app/mycrm/log" >> cdlog chmod +x cdlog 执行: ...
- Linux Shell编程(2)——第一个shell程序
在最简单的情况下,脚本程序不过是存储在一个文件里的系统命令列表.这至少让你执行它 时不必重新按顺序键入相同功能的命令序列.一个清空/var/log目录下的日志文件的脚本 # Cleanup # 必须以 ...
- 第一个shell程序
前言:我为什么又来学习shell呢?因为这个轻量级的编程小脚本语言能够帮我处理一些基于linux的复杂手工工作.真是一言难尽,学会一门又来一门!! 看了2天这个教程,试着写了一个小脚本,没啥技术含量, ...
- Linux shell程序一
设计一个Shell程序,在/$HONE/test目录下建立50个目录,即user1-user50, 并设置每个目录的权限,其中其他用户的权限为:读:文件所有者的权限为: 读.写.执行:文件所有者所在组 ...
- 学习Shell脚本编程(第4期)_在Shell程序中的使用变量
变量的赋值 变量的访问 变量的输入 4.1 变量的赋值 在Shell编程中,所有的变量名都由字符串组成,并且不需要对变量进行声明.要赋值给一个变量,其格式如下: 变量名=值 注意: 等号(= ...
- 学习Shell脚本编程(第3期)_在Shell程序中使用的参数
位置参数 内部参数 如同ls命令可以接受目录等作为它的参数一样,在Shell编程时同样可以使用参数.Shell程序中的参数分为位置参数和内部参数等. 3.1 位置参数 由系统提供的参数称为位置参数.位 ...
- 学习Shell脚本编程(第2期)_编写修改权限及执行Shell程序的步骤
编写Shell程序 执行Shell程序 Shell程序有很多类似C语言和其他程序设计语言的特征,但是又没有程序语言那样复杂.Shell程序是指放在一个文件中的一系列Linux命令和实用程序.在执行的时 ...
随机推荐
- 解决 spring cloud 项目的 com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect 错误信息
在项目中引入:引入hystrix依赖,如下 <dependency> <groupId>org.springframework.cloud</groupId> &l ...
- PHP and laravel知识点小小积累
function () use ($x, &$y){} 自从PHP5.3开始有了closure/匿名函数的概念,在这里的use关键词的作用是允许匿名函数capture到父函数scope 内存在 ...
- jquery mobile开发中常见的问题(转载)
1页面缩放显示问题 问题描述: 页面似乎被缩小了,屏幕太宽了. 处理方法: 在head标签内加入: <meta name="viewport" content="w ...
- 【NLP_Stanford课堂】语言模型4
平滑方法: 1. Add-1 smoothing 2. Add-k smoothing 设m=1/V,则有 从而每一项可以跟词汇表的大小相关 3. Unigram prior smoothing 将上 ...
- windows下安装配置RabbitMQ
安装部署 1.当前环境以及参考资料出处 部署环境:windows server 2008 r2 enterprise 官方安装部署文档:http://www.rabbitmq.com/install- ...
- Topshelf Configuration z
Topshelf Configuration While the Quickstart gives you enough to get going, there are many more featu ...
- Dz7.2 从获取uc key到getshell
0x01下午在群里看到的 问下朋友大概 然后用不生不熟的sqlmap在那跑–表能跑的出 列就GG了 然后没辙–晚上跑各大论坛逛了遍果然大神多 就慢慢的研究下了下 看了pt0n大牛的分析文 真心感叹这洞 ...
- C#图解教程读书笔记(第5章 方法)
类型推断和var关键字 从C#3.0开始,可以在变量声明的开始部分的的位置使用新的关键字var. Var关键字并不是某种特别类型的符号.它只是句法上的速记,表示任何可以从初始化的右边推断出的类型. V ...
- dynamic_cast动态转换
我们都知道dynamic_cast会在运行时进行类型检查,比较安全,static_cast静态转换,不安全 dynamic_cast转换的类型必须是个类,且这个类中必须有虚函数,为什么呢? 虚函数对于 ...
- 对HandlerExecutionChain类的理解分析
HandlerExecutionChain类比较简单,好理解. ==================================================================== ...