实现一个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命令和实用程序.在执行的时 ...
随机推荐
- sql中replace的用法
update 表名 set 字段名=REPLACE (字段名,'原来的值','要修改的值') 如:将tbl_user表的user_name字段中的大写的A替换成小写的a update tbl_stud ...
- Java xml 操作(Dom4J修改xml + xPath技术 + SAX解析 + XML约束)
1 XML基础 1)XML的作用 1.1 作为软件配置文件 1.2 作为小型的"数据库" 2)XML语法(由w3c组织规定的) 标签: 标签名不能以数字开头,中间不能有空格,区分大 ...
- Springboot基础知识
1.@RestController注解 Spring4之后新加入的注解,@RestController是@ResponseBody和@Controller的组合注解.(返回json需要@Respons ...
- win2008 svn 搬迁
公司说电脑不够用,要我们将本地开发用的服务器贡献出来给别人当办公电脑用..汗 将SVN从一个win2008服务器上搬迁到另一个win2008服务器上面. 先将服务器上面的配置好的svn 跟目录备份下来 ...
- Java初学者 编译能通过,但显示有错误,并且不会自动弹出方法的解决方法。
因为使用了 @Data注解,关于注解的作用尚未深入理解,此处先做一个记录. 解决方法是,添加lombok插件
- C++文件操作:打开文件和写入文件 zz
http://www.weixueyuan.net/view/5825.html 如果程序的运行结果仅仅显示在屏幕上,当要再次查看结果时,必须将程序重新运行一遍:而且,这个结果也不能被保留. 如果希望 ...
- 如何使用Adobe Reader复制PDF文档上的文字
PDF文档大家常用,但是有没有简单的方法能够提取PDF文档上的文字,然后使用呢?除了将PDF转换成Word,这里介绍一种更为简单实用的方法复制PDF文本文字,Adobe Reader是大家都常用的PD ...
- Redis入门实例(Redis+Sprint+maven创建工程)
一.>创建一个maven工程应用和相关配置:Redis_study,创建工程应用过程略 1.>配置pom.xml:文件内容如下 <project xmlns="http:/ ...
- Ios国际化翻译工具
IOS Translation Tool(IOS国际化翻译工具) 介绍 当IOS项目国际化的时候,手工去翻译每一个字符串是一件非常痛苦的事情.尤其是当项目中存在N多种语言.而且又很难保证,手工翻译的准 ...
- mongodb常用基础命令
查看当前使用的数据库,也可以直接用dbdb.getName() 显示当前db状态db.stats() 当前db版本 db.version() 查看当前db的链接机器地址db.getMongo() 查看 ...