Linux下c语言实现文件重定向功能

要求:job5增加文件重定向功能

在这里插入图片描述
在这里插入图片描述


思路

  • 1:分割字符串,提取信息
  • 2:在子进程中利用dup2命令按需要将标准输入输出重定向为指定的输入输出来源
    在这里插入图片描述

代码

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>

char op[100],t[100],p[100],r[100],q[100];
int st[10];

char *argv[10]; //把命令按空格划分
int argc=0;  //命令以空格分割后个单词个数
char *input; //输入地址,若无重定向,默认为NULL,若重定向了,则为重定向地址
char *output;//输出的地址,若无重定向,默认为NULL,若重定向了,则为重定向的地址
int mode=0; //如果重定向了输出,mode为0表示覆盖输出,为1表示追加输出

void parse_command(char *command)
{ //把输入的命令串处理“分割”,提取有用信息
	//init
	memset(st,0,sizeof(st));
	mode=0;
	input=NULL;
	output=NULL;
	//分割字符串
	char *ptr;
	int i=0;
	ptr=NULL;
	ptr=strtok(command," ");
	while(ptr)
	{
		argv[i]=ptr;
		++i;
		ptr=strtok(NULL," ");
	}
	argv[i]=NULL;
	argc=i;
	//遍历所有单词,查找重定向
	for(int i=1;i<argc;i++)
	{
		strcpy(op,argv[i]);
		
		if(op[0]=='<')  //重定向输入
		{  //提取地址
			int k=0;
			for(int j=1;op[j]!='\0';j++)
				r[k++]=op[j];
			r[k]='\0';
			if(r[0]!='/')
			{
				getcwd(q,sizeof(q));
				strcat(q,"/");
				strcat(q,r);
				input=q;
			}
			else
				input=r;
			st[i]=1;  //标记一下,后面要在命令串中把这个筛掉
			
		}
		else if(op[0]=='>')  //重定向输出
		{  //提取地址
			int k=0;
			for(int j=1;op[j]!='\0';j++)
			{
				if(op[j]=='>') continue;
				t[k++]=op[j];
			}
			t[k]='\0';
			
			if(t[0]!='/')
			{
				getcwd(p,sizeof(p));
				
				strcat(p,"/");
				strcat(p,t);
				
				output=p;
				
			}
			else
				output=t;
			if(op[1]=='>')
				mode=1;
			
			st[i]=1;//标记一下,后面要在命令串中把这个筛掉
		}		
	}
	//在分割后的命令串中把重定向了的部分筛掉,因为他们不能再往下传了
	char *tmp[10];     
	int k=0;
	for(int i=0;i<argc;i++)
	{
		if(!st[i])
			tmp[k++]=argv[i];
	}
	for(int i=0;i<k;i++)  //更新argc和argv
		argv[i]=tmp[i];
	argv[k]=NULL;
	argc=k;
	
}
void dump_command()  //输出字符串信息的,检验分割提取操作进行的是否正确
{
	printf("%d\n",argc);
	for(int i=0;i<argc;i++)
		printf("%s ",argv[i]);
	printf("\n");

	if(input!=NULL)
		printf("input:%s\n",input);
	else
		printf("input:NULL\n");

	if(output!=NULL)
		printf("output:%s mode:%d\n",output,mode);
	else
		printf("output:NULL\n");
}
void redirect()  //利用dup2进行重定向操作
{
	int fd1,fd2;
	if(input!=NULL)
	{
		fd1=open(input,O_RDONLY);
		if(fd1==-1)
		{
			printf("input fail open!\n");
			exit(0);
		}
		dup2(fd1,0);
	}
	if(output!=NULL)
	{
		
		if(mode==0)
			fd2=open(output,O_CREAT|O_TRUNC|O_WRONLY,0666);
		else
			fd2=open(output,O_CREAT|O_APPEND|O_WRONLY,0666);
		if(fd2==-1)
		{
			printf("output fail open\n");
			exit(0);
		}
		dup2(fd2,1);
	}	
}
void mysys(char *command)
{
	parse_command(command);
	
	if(strcmp(argv[0],"cd")==0)
		chdir(argv[1]);
	else if(strcmp(argv[0],"exit")==0)
		exit(0);
	else if(strcmp(argv[0],"pwd")==0)
	{
		char path[255];
		getcwd(path,sizeof(path));
		puts(path);
	}
	else
	{
		pid_t pid;
		pid=fork();
		if(pid==0)
		{
			redirect(); //注意,这里要在子进程中重定向
			int error=execvp(argv[0],argv);
			if(error<0)
			{
				perror("execv");
			}
		}
		wait(NULL);
	}
}
int main()
{
	char str[100];
	while(1)
	{
		printf(">");
		gets(str);
		mysys(str);
	}
	return 0;
}

注意点

  • 这里的文件重定向操作是在子进程里完成,而不是在主进程里完成
    如果是在主进程里进行操作,会影响主进程的后续操作。
  • 可以写一个parse_command函数,统一把字符串处理到位,把需要的信息都给处理出来,然后再写一个统一的输出函数dump_command()进行检验,把需要的信息可以开成全局变量,这样不用作为函数的参数传来传去,从全局的角度来看,更加直观,不易出错
  • 这里遍历命令单词串,查找带重定向符的单词时,最好要用不同的char型数组、指针,这样可以避免在循环中的先后操作而产生的覆盖问题,比如查找<操作符时,用了rq,而查找>操作符时,用了tp
  • 打开文件的时候,要注意选用的参数
    打开一个想要读的文件:fd1=open(input,O_RDONLY);
    打开一个想要覆盖写的文件:fd2=open(output,O_CREAT|O_TRUNC|O_WRONLY,0666);
    打开一个想要追加写的文件:fd2=open(output,O_CREAT|O_APPEND|O_WRONLY,0666);
  • echo命令而言,重定向输入符<是没有意义的,所以不需要考虑

2022/4.3更新

强化了程序的模块化,增强了代码的可读性

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
//这里的全局变量暂时还消不掉,因为如果用的是局部变量,那么在调用完返回的时候就被销毁了,但是其实是不能销毁的
char op[100];
char t[100],p[100],r[100],q[100];

struct command {
    int argc;
    char *argv[10];
    char *input;
    char *output;
    int mode;
};

int split(char *line, char *separator, char *word_array[])
{
    int word_count=0;
    char *word=NULL;

    word=strtok(line,separator);
    while(word) 
    {
        word_array[word_count]=word;
        word_count++;
        word=strtok(NULL,separator);
    }
    word_array[word_count]=NULL;
    return word_count;
}

void parse_command(char *command,struct command *order)
{	
	//define&init
	int st[10];
	memset(st,0,sizeof(st));
	order->mode=0;
	order->input=NULL;
	order->output=NULL;
	
	//split
	char *argv[10];
	int argc=split(command," ",argv);

	//test
	/*for(int i=0;i<argc;i++)
	{
		printf("%s ",argv[i]);
		fflush(stdout);
	}
	printf("\n");*/

	//search < > >>
	for(int i=1;i<argc;i++)
	{
		strcpy(op,argv[i]);
		//printf("[%s]\n",op);
		//fflush(stdout);
		if(op[0]=='<')
		{
			int k=0;
			for(int j=1;op[j]!='\0';j++)
				r[k++]=op[j];
			r[k]='\0';
			if(r[0]!='/')
			{
				getcwd(q,sizeof(q));
				strcat(q,"/");
				strcat(q,r);
				order->input=q;
			}
			else
				order->input=r;
			st[i]=1;
			//printf("1:%s\n",input);
		}
		else if(op[0]=='>')
		{
			int k=0;
			for(int j=1;op[j]!='\0';j++)
			{
				if(op[j]=='>') continue;
				t[k++]=op[j];
			}
			t[k]='\0';
			//printf("[%s]\n",t);
			//fflush(stdout);
			if(t[0]!='/')
			{
				getcwd(p,sizeof(p));
				//printf("[%s]\n",p);
				//fflush(stdout);
				strcat(p,"/");
				strcat(p,t);
				//printf("[%s]\n",p);
				//fflush(stdout);
				order->output=p;
				//printf("-1\n");
				//fflush(stdout);
			}
			else
				order->output=t;
			if(op[1]=='>')
				order->mode=1;
			//printf("[%s]\n",output);
			//fflush(stdout);
			st[i]=1;
		}		
	}
	int k=0;
	for(int i=0;i<argc;i++)
	{
		if(!st[i])
			order->argv[k++]=argv[i];
	}
	order->argv[k]=NULL;
	order->argc=k;
	//printf("k:%d\n",k);
	//printf("2:%s\n",input);*/
}
void dump_command(struct command *order)
{
	printf("%d\n",order->argc);
	for(int i=0;i<order->argc;i++)
		printf("%s ",order->argv[i]);
	printf("\n");
	//printf("-1\n");
	//fflush(stdout);
	if(order->input!=NULL)
		printf("input:%s\n",order->input);
	else
		printf("input:NULL\n");
	//printf("0\n");
	//fflush(stdout);
	if(order->output!=NULL)
		printf("output:%s mode:%d\n",order->output,order->mode);
	else
		printf("output:NULL\n");
}

void redirect(struct command *order)
{
	int fd1,fd2;
	if(order->input!=NULL)
	{
		fd1=open(order->input,O_RDONLY);
		if(fd1==-1)
		{
			printf("input fail open!\n");
			exit(0);
		}
		dup2(fd1,0);
	}
	if(order->output!=NULL)
	{
		//printf("%s\n",output);
		if(order->mode==0)
			fd2=open(order->output,O_CREAT|O_TRUNC|O_WRONLY,0666);
		else
			fd2=open(order->output,O_CREAT|O_APPEND|O_WRONLY,0666);
		if(fd2==-1)
		{
			printf("output fail open\n");
			exit(0);
		}
		dup2(fd2,1);
	}	
}
void mysys(char *command)
{
	struct command order;
    parse_command(command, &order);
	
	//dump_command(&order);

	if(strcmp(order.argv[0],"cd")==0)
		chdir(order.argv[1]);
	else if(strcmp(order.argv[0],"exit")==0)
		exit(0);
	else if(strcmp(order.argv[0],"pwd")==0)
	{
		char path[255];
		getcwd(path,sizeof(path));
		puts(path);
	}
	else
	{
		pid_t pid;
		pid=fork();
		if(pid==0)
		{
			redirect(&order);    //redirect
			int error=execvp(order.argv[0],order.argv);
			if(error<0)
				perror("execv");
		}
		wait(NULL);
	}
}
int main()
{
	char str[100];
	while(1)
	{
		printf(">");
		gets(str);
		mysys(str);
	}
	return 0;
}
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alkali!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值