赞
踩
1、实验名称:shell命令解释系统设计实验
2、实验要求:
问题 A:
实现一个能处理前后台运行命令的 shell。
问题 B:
实现一个带有管道功能的 shell。
问题 C:
实现一个能处理 I/O 重定向的 shell。
问题 D:
实现一个能在一行上处理多条命令的 shell。
将问题 A-D 集中到一个 shell 解析程序中。
3、解决思路
第一步解决问题D。一行上多条命令使用 ; 分开,;两边可以有空格,也可以没有,或者都考虑(健壮性),本方案采用没空格。根据 ; 将其拆分成多条命令,逐条执行即可。
第二步解决问题A。让进程在后台运行,意味着父进程不需要等待子进程运行完毕。否则使用waitpid()函数让父进程等待一下即可。
第三步解决输出重定向,即问题C。根据定向符 > 获得后面的文件名。使用open()打开,使用dup2()函数将标准输出重定向到该文件。那么该进程的输出都将添加到文件中,而不是终端。通常在子进程内进行相应操作。
第四步解决输出重定向,问题C。根据输入定向符 < 获得后面文件名。使用open打开。使用dup2将标准输入重定向到该文件,那么该进程将使用该文件作为输入,而不是终端。
第五步解决管道问题。此处使用匿名管道。匿名管道使用pipe(fds)创建。fds为int类型长度为2的数组。fds[0]为读取数据端。fds[1]为写入数据端。创建两个进程一个管道。进程1关闭读端,将标准输出重定向到管道写端,将数据暂时保存在管道内。进程2关闭写端,将标准输入重定向到管道读端,将管道内数据读出,完成进程通信。
缺点一是支持格式有限。比如
普通命令:ls -a /bin //加粗部分必须为全路径
输出重定向:ls /bin > /home/me/shiyan/test.c //>两侧必须空格
输入重定向:cat < /home/me/shiyan/test.c
管道:cat /home/me/shiyan/test.c | sort //管道两端命令不支持出现重定向
//管道符号两侧必须有空格
后台运行:ls /bin > /home/me/shiyan/test.c& //&符号必须紧跟着
多条命令:ls /home;ls /bin //多条命令分号两边无空格
所有测试命令必须严格遵循上面的格式,否则将可能导致错误的运行结果。
另外一个缺点是,没有完整测试所有linux命令,只测试了部分简单常用命令,因此可能部分命令不支持。
4、实现代码
#include <stdio.h>
#include <string.h> //strlen()
#include <unistd.h> //fork()
#include <sys/types.h> //pid_t
#include <sys/wait.h> //waitpid()
#include <stdlib.h> //exit()
#include <fcntl.h> //open()
#define MAXLEN 80
#define MAXPARA 8
int checkpipe(char *command,int comlen)//判断是否包含|
{
int i;
for(i=0; i<comlen; i++)
{
if(command[i] == '|')
return i;
}
return -1;
}
int checkout(char *command,int comlen)//判断是否包含>
{
int i;
for(i=0; i<comlen; i++)
{
if(command[i] == '>')
return 1;
}
return 0;
}
int checkin(char *command,int comlen) //判断是否包含<
{
int i;
for(i=0; i<comlen; i++)
{
if(command[i] == '<')
return 1;
}
return 0;
}
void runcommand(char *command)
{
int comlen = strlen(command);
int backrun = 0;
char *argv1[MAXPARA],*argv2[MAXPARA];
int index1,index2;
int i,fd,pipei,fds[2];
char *filename;
pid_t p1,p2;
if(command[comlen-1] == '&')//是否后台运行
{
backrun = 1;
command[comlen-1] = 0;
comlen--;
}
pipei = checkpipe(command,comlen);
if(pipei > -1)//有管道
{
command[pipei-1] = 0;
command[pipei] = 0;
command[pipei+1] = 0;
index1 = 0;
argv1[index1++] = command;
for(i=0; i<pipei-1; i++)
{
if(command[i] == ' ')
{
command[i] = 0;
argv1[index1++] = &command[i+1];
}
}
argv1[index1] = NULL;
i = pipei + 2;
index2 = 0;
argv2[index2++] = &command[i];
for(; i<comlen; i++)
{
if(command[i] == ' ')
{
command[i] = 0;
argv2[index2++] = &command[i+1];
}
}
argv2[index2] = NULL;
//命令处理完毕,建立管道,进程
pipe(fds);
p1 = fork();
if(p1 == 0)
{
close(fds[0]);//关闭读端
close(STDOUT_FILENO);
dup2(fds[1],STDOUT_FILENO);//重定向
close(fds[1]);
execvp(argv1[0],argv1);//该函数执行后进程结束
}
else
{
if(backrun == 0)
waitpid(p1,NULL,0);
p2 = fork();
if(p2 == 0)
{
close(fds[1]);
close(STDIN_FILENO);
dup2(fds[0],STDIN_FILENO);
close(fds[0]);
execvp(argv2[0],argv2);
}
else
{
//父进程关闭管道,让两个子进程进行通信
close(fds[0]);
close(fds[1]);
waitpid(p2,NULL,0);
}
}
}
else
{
if(checkout(command,comlen) == 1)//输出重定向
{
index1 = 0;
argv1[index1++] = command;
for(i=0; i<comlen; i++)
{
if(command[i] == ' ')
{
command[i] = 0;
if(command[i+1] == '>')
break;
argv1[index1++] = &command[i+1];
}
}
argv1[index1] = NULL;
i = i + 3;
filename = &command[i];
p1 = fork();
if(p1 == 0)
{
fd = open(filename,O_WRONLY);
close(STDOUT_FILENO);
dup2(fd,STDOUT_FILENO);
close(fd);
execvp(argv1[0],argv1);
}
else
{
if(backrun == 0)
{
waitpid(p1,NULL,0);
}
}
}
else if(checkin(command,comlen) == 1)//输入重定向
{
index1 = 0;
argv1[index1++] = command;
for(i=0; i<comlen; i++)
{
if(command[i] == ' ')
{
command[i] = 0;
if(command[i+1] == '<')
break;
argv1[index1++] = &command[i+1];
}
}
argv1[index1] = NULL;
i = i + 3;
filename = &command[i];
p1 = fork();
if(p1 == 0)
{
fd = open(filename,O_RDONLY);
close(STDIN_FILENO);
dup2(fd,STDIN_FILENO);
close(fd);
execvp(argv1[0],argv1);
}
else
{
if(backrun == 0)
{
waitpid(p1,NULL,0);
}
}
}
else//普通命令
{
index1 = 0;
argv1[index1++] = command;
for(i=0; i<comlen; i++)
{
if(command[i] == ' ')
{
command[i] = 0;
argv1[index1++] = &command[i+1];
}
}
argv1[index1] = NULL;
p1 = fork();
if(p1 == 0)
{
execvp(argv1[0],argv1);
}
else
{
if(backrun == 0)
{
waitpid(p1,NULL,0);
}
}
}
}
}
int main()
{
char linebuf[MAXLEN], *command;
int buflen,i;
while(1)
{
write(STDOUT_FILENO,"$",1);
fgets(linebuf,MAXLEN,stdin);
buflen = strlen(linebuf);
if(linebuf[buflen-1] == '\n')
{
linebuf[buflen-1] = 0;
buflen--;
}
if(strcmp(linebuf,"exit") == 0)
exit(0);
else
{
command = linebuf;
for(i=0; i<buflen; i++)
{
if(linebuf[i] == ';')
{
linebuf[i] = 0;
runcommand(command); //循环执行多条命令
command = &linebuf[i+1];
}
}
runcommand(command); //多条命令最后一条
}
}
return 0;
}
5、程序的健壮性优化请参阅:
http://www.powerxing.com/unix-simple-shell-with-argv/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。