目录
0.前提
编辑
1.重定向
1.1重定向的本质
1.2dup2
1.3模拟实现输出重定向 >
1.4模拟实现追加重定向 >>
1.5模拟实现输入重定向 <
2.让minishell支持重定向
0.前提
文件描述符的分配规则:
在文件描述符表里面,从小到大按照顺序寻找最小的且没有被占用的fd。
1.重定向
1.1重定向的本质
上层使用的fd不变,在内核中更改fd对应的struct file*的地址。
1.2dup2
让newfd变成oldfd的拷贝,如果需要关闭newfd.
1.3模拟实现输出重定向 >
1.4模拟实现追加重定向 >>
1.5模拟实现输入重定向 <
2.让minishell支持重定向
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<errno.h>#define B_NUM 1024
#define ARGV_NUM 10
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3char buffer[B_NUM];
char* myargv[ARGV_NUM];
int quitcode=0;
int quitsignal=0;int redirType=NONE_REDIR;
char* refilename=NULL;void Commandcheck(char* command)
{assert(command);//判断是否有重定向,有是哪种char* begin=command;char* end=command+sizeof command-1;while(begin<end)//while((*begin)!='\0'){if(*begin=='>'){*begin='\0';begin++;if(*begin=='>'){redirType=APPEND_REDIR;begin+=2;refilename=begin;}else{redirType=OUTPUT_REDIR;refilename=(++begin);break;}}if(*begin=='<'){*begin='\0';redirType=INPUT_REDIR;begin+=2;refilename=begin;break;}begin++;}return ;
}int main()
{while(1){redirType=NONE_REDIR;refilename=NULL;errno=0;printf("【用户名@服务器 当前目录】$");fflush(stdout);char* s=fgets(buffer,sizeof (buffer)-1,stdin);assert(s!=NULL);buffer[strlen (buffer)-1]=0;(void)s;//和重定向区分开Commandcheck(buffer);// printf("切割成功:命令 %s 文件 %s\n",buffer,refilename);//切割字符串myargv[0]=strtok(buffer," ");int i=0;if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0){myargv[++i] = (char*)"--color=auto";}while(myargv[i]!=NULL){myargv[++i]=strtok(NULL," ");}//实现cd功能if(myargv[0] != NULL &&strcmp(myargv[0],"cd")==0){if(myargv[1]!=NULL){chdir(myargv[1]);}continue;}//实现echo $?功能if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0){if(strcmp(myargv[1],"$?")==0){//输出上一个进程的退出码printf("退出码为:%d\n",quitcode);}else{printf("%s\n",myargv[1]);}continue;}pid_t id =fork();assert(id!=-1);if(id==0){//子进程//如果有文件打开文件switch(redirType){case NONE_REDIR:break;case INPUT_REDIR:{int fd=open(refilename,O_RDONLY);if(fd < 0){ perror("open");exit(errno);}dup2(fd,0);}break;case OUTPUT_REDIR:case APPEND_REDIR:{int flag=O_WRONLY|O_CREAT;if(redirType==OUTPUT_REDIR){flag|=O_TRUNC;}else{flag|=O_APPEND;}umask(0);int fd=open(refilename,flag,0666);if(fd < 0){ perror("open");exit(errno);}dup2(fd,1);}break;default:break;}//程序替换execvp(myargv[0],myargv); //如果走到这里表示替换失败exit(1);}//父进程int status=0;pid_t ret= waitpid(id,&status,0);assert(ret > 0);(void)ret;quitcode=(status>>8)&0XFF;quitsignal= (status & 0x7F);}return 0;
}
注意:每次父进程循环都要记得刷新 redirType 、redirFile、errno
最后
加油