目录
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 3
char 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
最后
加油