实现一个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命令和实用程序.在执行的时 ...
随机推荐
- base64编码 的 图片 另存为下载
功能描述: 有一段base64字符串的图片,将其保存下载为png图片! 可以: 直接 a 链接下载: <a id="tttt" download="1.jpg& ...
- gulpfile配置
/** * 只包含合并压缩混淆,监听服务 */// 引入gulp模块var gulp = require('gulp'); // 引入其他模块var less = require('gulp-less ...
- servlet中cookie和session操作
1.1 软件中的会话 一次会话: 打开浏览器 -> 访问一些服务器内容 -> 关闭浏览器 登录场景: 打开浏览器 -> 浏览到登陆页面 -> 输入用户名和密码 -> 访问 ...
- Java 之数组(4)
什么是数组: 问:编写代码保存 4 名学生的考试成绩. 答:简单啊,定义 4 个变量呗 问:那“计算全年级 400 名学生的考试成绩”,肿么办 答: ....... 数组,就可以帮助你妥妥的解决问题啦 ...
- C# Winform DataGridView获取单元格的值
1,可以直接通过DataGridView的重载运算符[]直接获取 使用方法: dataGridView[columnIndex][rowsIndex].Value.ToString().//colum ...
- 移动端fixed的元素抖动的问题
工作中发现,给一个元素添加fixed属性,让它固定在窗口某个位置,直接加fposition:fixed属性就能实现这个效果: 在安卓手机上的效果都比较好,但是ios系统的个别浏览器兼容性就不好,如QQ ...
- January 31 2017 Week 5 Tuesday
Better to get hurt by the truth than comforted with a lie. 被真相伤害好过被谎言安慰. For ourselves, we need to b ...
- 分享上传图片shell的绕过过滤的几种方法
一般网站图片上传功能都对文件进行过滤,防止webshell写入.但不同的程序对过滤也不一样,如何突破过滤继续上传? 本文总结了七种方法,可以突破! 1.文件头+GIF89a法.(php)//这个很好理 ...
- API代理网关和OAuth2.0授权认证框架
API代理网关和OAuth2.0授权认证框架 https://www.cnblogs.com/bluedoctor/p/8967951.html 1,授权认证与微服务架构 1.1,由不同团队合作引发的 ...
- 不错的.net开源项目
Json.NET http://json.codeplex.com/ Json.Net是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单.通过Lin ...