什么是重定向?
什么是清空重定向与追加重定向?
" > " " >>" 这两个符号相信大家都不陌生,有什么作用呢?
举个李子:
通常我们在 linux 终端敲下开机必敲的命令估计就是:
ls 或者 ls -l//查看当前目录下的文件 /查看当前目录下文件的详细信息
首先 vi 一个新的 q.txt的文件,打开 wq 保存退出(对,就是啥写不写),然后让我养的cat去看一下里面的东西
是的,和我的钱包一样,空空如也;
再使用命令:
ls -l > q.txt
然后再让我的猫看一下
amazing ! 为什么之前输出在屏幕上的东西会被写进了 q.txt 这个文件?
重定向也就是:将数据不再写入原本的文件而是写入新的指定得文件中
这就是 > 符号的作用 – 清空重定向
这里也就是这个文件 q.txt
而 >> 是追加重定向,也就是将内容追加到指定的地方,例如:
这里其实可以很容易想到是,将原本要打印在屏幕上的当前目录下的文件信息,写入到指定的q.txt文件中,这其实就是IO流的重定向。
我们知道使用open打开一个文件的时候会返回一个文件描述符,那么这个文件描述符是什么呢?
是一个非负整数–操作文件的句柄
文件描述符其实就是内核中一个进程打开的文件描述信息表的下标—通过这个下标可以在内核中找到相应的文件描述信息,通过这个描述信息可以实现文件的操作
这就是我们一般打开文件操作时:
int fd;
fd=open("pathname",mode);
read(fd,buf,count);
如上图:
通过文件描述符就可以找到对应的数组下标的位置,找到文件的描述信息,从而找到这个文件,这样就可以对文件进行操作,这也是文件打开了为什么要关闭,如果不关闭,一直打开最后就会导致这个数组满甚至溢出,文件描述符满了,就无法再打开新的文件了。
就会出现以下的情况
但是这里还有一个奇怪的现象,每次打开的文件返回的第一个文件描述符都是 3 这是为什么呢?
例如:
#include
#include
int main()
{
int fd=open("./txt.txt",O_RDONLY);
printf("%d\n",fd);
close(fd);
return 0;
}
结果:
然后再打开一个,文件描述符就是4 依次类推,那么前面的 0 1 2 去那了呢?
其实 文件描述符的 0 1 2 分别对应着:
一个程序运行起来,进程会默认打开三个文件:
标准输入 – 0
标准输出 – 1
标准错误 – 2
文件描述符有个分配规则:最小未使用
所以怪不得每次打开就是 3 ,那这个和我们今天要讲的 清空重定向和追加重定向有啥关联呢?
接下来我们试一试下面这样的操作:
#include
#include
#include
int main()
{
//close(0); 关闭0号文件描述符 也就是关闭了标准输入
close(1);//关闭 1 号文件描述符 也就是关闭了标准输出 屏幕将没有输出
//close(2); 关闭 2 号文件描述符 也就是关闭了标准错误
int fd1=-1;
fd1=open("./txt.txt",O_RDWR|O_CREAT,0644);
if(fd1<0)
{
perror("open error\n");
}
dup2(fd1,1);//将一号文件描述符重定向到fd1所指向的文件中
printf("fd1=%d\n",fd1);
fflush(stdout);//刷新标准输出到缓冲区
close(fd1);
return 0;
}
这时我们发现结果:txt.txt文件中是 fd=1 这个fd=1 原本是我们程序中的输出信息,要打印在屏幕上,这里却没有打印而在文件 txt.txt中出现了,这其实就实现了重定向;
这里讲一个重定向的函数:
int dup2(int oldfd, int newfd);//将newfd指向的文件指向到oldfd指向的文件
在借助之前学习过的minishell的知识,只需要以下几步即可,不太清楚minishell的可以看以前的博客:
[Linux实现一个minishell](https://blog.csdn.net/weixin_42307601/article/details/107183694)
1.对输入的命令进行解析,判断是 “>” 清空重定向还是 “>>” 追加重定向
2.解析出重定向之后的文件名称
3.子进程进行程序替换,使用dup2函数进行重定向的实现
具体代码如下:
#include
#include
#include
#include
#include
int main()
{
while(1)
{
printf("hfy@minishell $:");
fflush(stdout);//刷新缓冲区
//1.等待标准输入
char buf[1024]={0};//用来存放获取的命令
fgets(buf,1023,stdin);//获取输入的命令
buf[strlen(buf)-1]='\0';
//1.5 对需要重定向的文件进行解析
char *ptr1=buf;
int ret1=0;//用于标志是清空重定项还是追击重定项的标志位
char *retptr=NULL;//定义一个存储需要重定项的文件名指针
while(*ptr1!='\0')
{
if(*ptr1=='>')
{
ret1=1;//如果是清空重定项将标志位置为 1
*ptr1='\0';//这里说明命令解析完毕
ptr1++;
if(*ptr1=='>')
{
ret1=2;//如果是追加重定项 将标志位置为 2
ptr1++;
}
while(*ptr1==' '&&*ptr1!='\0') ptr1++;
retptr=ptr1;//这个指针指向需要文件名的起始位置
while(*ptr1!=' '&&*ptr1!='\0') ptr1++;//将文件名走完
*ptr1='\0';//将最后一个置为\0
}
ptr1++;
}
//2.对输入的进行解析
char *argv[32]={NULL};//定义的一个二维数组
int argc=0;
char *ptr=buf;
while(*ptr!='\0')
{
if(*ptr!=' ')//获取输入的命令 存入数组中
{
argv[argc]=ptr;
argc++;
int i=0;
while(*ptr!=' '&& *ptr!='\0')
{
ptr++;
}
*ptr='\0';
}
ptr++;
}
argv[argc]=NULL;//最后一个位置参数的下一个位置置空NULL 这是因为函数要求传递的字符数组的最后一个必须为NULL
int i=0;
for(i=0;i<argc;i++)
{
printf("argv[%d]=argv[%s}\n",i,argv[i]);//可以对输入的命令进行查看
}
//3.创建子进程 并完子进程的程序替换
pid_t pid=fork();
if(pid==0)
{
if(ret1==1)//这是一个清空重定向
{
int fd=open(retptr,O_WRONLY|O_CREAT|O_TRUNC,0664);
dup2(fd,1);//将标准输入重定向到retptr 也就是我们刚解析的文件名,那么原本要进行输出打印的内容就会被写入到该文件中
}
else if(ret1==2)//表示这是一个追加重定向
{
int fd=open(retptr,O_WRONLY|O_CREAT|O_APPEND,0664);//追加重定项,改变上面个的写入方式即可
dup2(fd,1);//将标准输入重定向到retptr
}
execvp(argv[0],argv);//程序替换成功就去运行程序了 失败了返回负一 成功的时候没有返回值
perror("execvp error");//如果程序走到这里就表明程序替换失败了打印上一次系统调用接口使用的错误原因
exit(0);
}
//4.进程等待
wait(NULL);
}
return 0;
}
测试用例:
清空重定向:ls - l > q.txt
结果:
测试用例:追加重定向 ls -l q.txt >> q.txt
结果: