实验内容:
你的任务是实现⼀个在 Linux 系统下运⾏的简单 Shell,它能够实现如下功能:

  1. 运⾏带参数的命令
  2. 重定向输⼊输出(仅实现 < 与 > 即可)
  3. 建⽴多级连接的双向管道,实现 | 左侧的标准输出传递给右侧作为其标准输⼊
  4. 能够响应 Linux 中的信号,即当执⾏命令的进程收到 SIGINT 时会被杀死,⽽ Shell 本身不会被杀死
    其余的 Shell 特性不需要实现。以下是示例(这⾥的 # 是命令提示符,类似于 bash 中的 $ )。
# echo hello there
hello there
# echo something > file.txt
# cat file.txt
something
# cat file.txt | wc
 1 1 10
# echo echo hello | ./msh
hello
# # #
#include <sys/types.h> 
#include <wait.h> 
#include <unistd.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include<string.h> 
#include<fcntl.h>

pid_t parentpid,childpid;
// 进程⾃定义的键盘中断信号处理函数
typedef void (*sighandler_t) (int); void sigcat() 
{ 
	if(getpid()==parentpid)
	kill(childpid,SIGINT);
}
//解析字符串
int get_order(char *buf,char**argv)
{
	int argc=0; 
	int flag=0;
	int len=strlen(buf); 
	for(int i=0;i<len;i++)
	{
		if(buf[i]!=' '&&flag==0)
		{
			argv[argc++]=buf+i; 
			flag=1;
		}
		else if(buf[i]==' ')
		{
			flag=0; 
			buf[i]=0;
		}
	}
	argv[argc]=NULL;
	//for(int i=0;i<argc;i++)printf("%s\n",argv[i]); return argc;
}
//执行无管道的指令
void solve1(char *cmd)
{
	char *argv[1024];
	int argc=get_order(cmd,argv); 
	char *infile=NULL,*outfile=NULL; //记录文件名
	int inflag=0,outflag=0;//标记是否有重定向
	int targc=argc;//用来记录'<'或'>'的前一个位置
	for(int i=0;i<argc;i++)
	{
		if(strcmp(argv[i],"<")==0)
		{ 
			if(i+1==argc)
			{
				printf("File name does nat exist.\n"); 
				exit(1);
			}
			else{
				infile=argv[++i]; 
				inflag=1; 
				if(targc==argc)targc=i-1;
			}
		}
		if(strcmp(argv[i],">")==0)
		{ 
			if(i+1==argc){
				printf("File name does nat exist.\n"); 
				exit(1);
			}
			else{
				outfile=argv[++i]; 
				outflag=1; 
				if(targc==argc)targc=i-1;
			}
		}
	}
	//处理重定向输入的目标文件不存在的情况
	if(inflag==1)
	{
		FILE *fp=fopen(infile,"r"); 
		if(fp==NULL)
		{
			printf("The input file not exist.\n"); 
			exit(1);
		}
		fclose(fp);
	}
	//for(int i=0;i<argc;i++)printf("%s\n",argv[i]); 
	childpid=fork();
	if(childpid<0)
	{
		printf("create process failed!\n"); 
		exit(1);
	}
	else if(childpid==0)
	{
		if(inflag==1)
		{//在分配⽂件描述符的时候会选择最⼩的未被分配的描述符
			close(0); //关闭了0文件标识符,下面open的时刻就会把0文件标识符与文件关联
			if(open(infile,O_RDONLY)!=0)
			{
				fprintf(stderr,"testsh:open!=0\n"); 
				exit(-1);
			}
		}
		if(outflag==1)
		{
			close(1); 
			if(open(outfile,O_WRONLY|O_CREAT|O_TRUNC,0777)!=1)
			{
				fprintf(stderr,"testsh:open!=1\n"); 
				exit(-1);
			}
		}
		char *temp[1024];
		for(int i=0;i<targc;i++)temp[i]=argv[i]; 
		temp[targc]=NULL;
		
		execvp(temp[0],temp);
	}
	else{
		int status; 
		waitpid(childpid,&status,0);
	}
}
//处理包含管道的指令
void solve(char *cmd)
{
	//无管道指令
	if(strstr(cmd,"|")==NULL)
	{
		solve1(cmd); 
		return;
	}
	char *now;
	char *next=NULL;
	now =strtok_r(cmd,"|",&next); //根据'|'符号,切割字符串
	if(*next==0){
		printf("The end need a filename after |.\n"); 
		exit(1);
	}
	int pipe1[2]; 
	if(pipe(pipe1)<0){
		printf("create pipe failed.\n"); 
		exit(1);
	}
	childpid=fork(); 
	if(childpid<0){
		printf("create fork failed.\n"); 
		exit(1);
	}
	else if(childpid==0)
	{
		close(pipe1[0]);
		dup2(pipe1[1],1); //将文件标识符1和管道关联
		solve1(now); 
		exit(0);
	}
	else{
		int status; 
		waitpid(childpid,&status,0);
		
		close(pipe1[1]);
		dup2(pipe1[0],0);
		
		solve(next); 
		close(pipe1[0]);
	}
}
int main()
{
	char buf[1024]; 
	char *argv[1024];
	parentpid = getpid();
	signal(SIGINT, (sighandler_t)sigcat); 
	while(1)
	{
		memset(buf,0,sizeof(buf)); 
		//printf("my pid is %d\n",getpid()); 
		//printf("myshell #");
		char c; 
		int len=0;
		while(scanf("%c",&c)!=EOF)
		{
			if(c!='\n'&&c!='\0')buf[len++]=c; 
			else break;
		}
		fflush(stdin); 
		if(*buf=='\0')exit(1);
		
		//printf("%s\n",buf); 
		char temp[1024]; 
		strcpy(temp,buf);
		
		int stdinput=dup(0); 
		int stdoutput=dup(1);
		
		solve(buf);
		
		dup2(stdinput,0); 
		dup2(stdoutput,1);
	}
	return EXIT_SUCCESS;
}
Logo

更多推荐