dup、dup2实现文件描述符重定向(标准输入、标准输出、标准错误输出)
目录
dup函数
dup2函数
重定向标准输入
重定向标准输出
重定向标准错误输出
重定向恢复
总结
在前文中,可以知道,文件描述符实际上是指向文件表项的指针数组索引,也就相当于每个文件描述符都对应一个文件表项,最终对应一个文件,而文件描述符重定向,则是让一个文件描述符指向另一个特定的文件表项,最终使得不同的文件描述符指向同一个文件表项,常用到的函数就是dup、dup2以及fcntl等函数。
此外,对于linux下系统默认的描述符,0、1、2是最特殊的,它们分别对应了标准输入、标准输出和标准错误输出,如下所示:
名称 | 文件描述符 | 含义 | 设备 | 说明 |
---|---|---|---|---|
STDIN | 0 | 标准输入 | 键盘 | 获取执行时所要的输入数据 |
STDOUT | 1 | 标准输出 | 显示器 | 输出执行后的输出结果 |
STDERR | 2 | 标准错误输出 | 显示器 | 输出执行时的错误信息 |
以下主要围绕这3个系统默认的文件描述符来讲一下dup函数和dup2函数。
dup函数
dup函数的原型为int dup(int oldfd);
该函数的作用是,返回一个新的文件描述符(可用文件描述符的最小值)newfd,并且新的文件描述符newfd指向oldfd所指向的文件表项。如以下调用形式:int newfd = dup(oldfd);假设调用时oldfd = 1(即系统默认的标准输文件描述符),调用后newfd = 3(假设),那么文件描述符3所对应的文件表项就是文件描述符1对应的文件表项。
通过这样,就可以把一个普通的文件描述符(这里为3)重定向到标准输出文件描述符(文件描述符为1),文件描述符为1对应的设备是显示器,也就是说,向文件描述符1进行write,就可以打印在显示器上,此时如果将一个文件描述符3通过dup重定向到文件描述符1上,那么也就相当于文件描述符3对应的设备也是显示器,那么向文件描述符3进行write,最终结果也会打印在显示器上,如下所示:
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <fcntl.h>
#include <errno.h>int main()
{const char *str = "hello world!\n";int newfd = -1;newfd = dup(1); //将newfd重定向到标准输出std::cout<<"newfd = "<<newfd<<std::endl;write(newfd,str,strlen(str)); //向newfd中写入字符串close(newfd);return 0;
}
可以看到,虽然是对newfd进行了write,但是最终字符串是打印到了屏幕上,即newfd重定向到了标准输出上。
dup2函数
dup2函数原型为int dup2(int oldfd,int newfd);
dup2函数与dup函数的功能大致相同,不过仍然是有差别的。
dup函数是返回一个最小可用文件描述符newfd,并让其与传入的文件描述符oldfd指向同一文件表项;
而dup2则是直接让传入的参数newfd与参数oldfd指向同一文件表项,如果newfd已经被open过,那么就会先将newfd关闭,然后让newfd指向oldfd所指向的文件表项,如果newfd本身就等于oldfd,那么就直接返回newfd。因此,传入的newfd既可以是open过的,也可以是一个任意非负整数,总之,dup2函数的作用就是让newfd重定向到oldfd所指的文件表项上,如果出错就返回-1,否则返回的就是newfd。如下所示:
#include <string.h>
#include <unistd.h>
#include <fcntl.h>int main()
{const char *str = "hello world!\n";dup2(1,5); //将“5”重定向到标准输出write(5,str,strlen(str)); //向文件描述符5写入数据close(5);return 0;
}
可以看到,虽然这里的“5”并没有对应一个打开的文件,但是也可以只用dup2函数,让“5”作为一个文件描述符重定向到标准输出,最终向文件描述符“5”进行write,就相当于是向标准输入的设备显示器进行输出,也就在屏幕中打印出来字符串。
通过上面的阐述,接下来看看如何重定向标准输入、标准输出以及标准错误输出。
重定向标准输入
前面提到,标准输入所对应的设备是键盘,也就是说,标准输入相应的文件描述符,当它进行read时,实际上是read键盘输入的数据,而如果不想让键盘作为标准输入呢?比如说让程序从某个文件中读取输入数据,这就需要重定向标准输入了。
举个例子,我用一个文件描述符3对应一个打开的文件A,然后调用dup2(3,0)函数,这样就使得标准输入文件描述符0重定向到了文件描述符3所对应的文件表项上,原本应该从键盘获取数据,现在变成从文件A获取数据,此时如果调用read或者其他标准输入函数如cin、getline、getchar等函数,都是从文件A中读取数据。如下所示:
先向外部文件test.txt中写入以下测试内容:
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <fcntl.h>using namespace std;int main()
{string rdstr;int fd = -1;if((fd = open("test.txt",O_RDWR)) == -1){cout<<"open failed !"<<endl;return -1;}dup2(fd,0); //重定向标准输入到外部文件test.txt中while(getline(cin,rdstr)) //用getline从标准输入中获取数据{cout<<rdstr<<endl; //通过标准输出将读入的数据打印出来}close(fd);return 0;
}
可以看到,这里虽然调用了getline函数,它会从标准输入中获取数据,在一般情况下会阻塞等待键盘输入,但是由于这里标准输入重定向到了外部文件test.txt中,因此getline就直接从test.txt中获取每一行数据,与键盘输入无关。
重定向标准输出
标准输出的设备是显示器,通过标准输出文件描述符1进行write时,数据会直接输出到显示器上。那么如果不想让标准输出输出到显示器上呢?比如说想让cout、printf直接将数据输出到文件中,那么就需要重定向标准输出了。如下所示:
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <fcntl.h>
#include <errno.h>using namespace std;int main()
{int fd = -1;if((fd = open("test.txt",O_RDWR|O_CREAT|O_TRUNC)) == -1) //先将test.txt的文本内容清空{cout<<"open failed !"<<endl;return -1;}dup2(fd,1); //重定向标准输出到外部文件test.txtcout<<"重定向标准输出测试!"<<endl; //向标准输出输出数据close(fd);return 0;
}
将标准输出重定向到外部文件test.txt后,原本标准输出会将数据输出到显示器上,重定向后就将数据输出到了外部文件中。虽然使用cout进行了标准输出,但是程序执行后,显示器上并无任何输出,通过more查看外部文件内容,可以看到cout的数据最终输出到了外部文件中。
重定向标准错误输出
标准错误输出实际上与标准输出类似,都是将数据输出到显示器上,只不过标准错误输出是输出错误信息,C语言中常用的错误输出就是perror了,如下面打开一个不存在的文件,就会直接在显示器上输出报错信息:
#include <fcntl.h>
#include <stdio.h>using namespace std;int main()
{open("123.txt",O_RDWR);perror(NULL);return 0;
}
现在想把错误信息直接输出到外部文件中,就可以将标准错误输出进行重定向,如下所示:
#include <unistd.h>
#include <iostream>
#include <fcntl.h>
#include <stdio.h>
using namespace std;int main()
{int fd = -1;if((fd = open("test.txt",O_RDWR|O_CREAT|O_TRUNC)) == -1) //打开并清空文件{cout<<"open failed !"<<endl;return -1;}dup2(fd,2); //重定向标准错误输出到外部文件中open("123.txt",O_RDWR); //打开一个不存在的文件perror("重定向标准错误输出测试"); //输出错误信息close(fd);return 0;
}
可见,重定向标准错误输出之后,执行程序并不会输出任何信息,但是查看test.txt文件发现,错误信息写入到了该文件中。
通过3个重定向的例子也可以发现dup2相较于dup函数的好处:可以让一个已打开的文件描述符(如这里的0、1 、2)重定向到另一个已打开的文件描述符上(如外部文件)。
重定向恢复
在进行重定向后,如果想要恢复到重定向之前的状态,可以在重定向之前用dup函数保留该文件描述符对应的文件表项,然后在需要恢复重定向的时候使用dup2重定向到原来的文件表项,以重定向后恢复标准输出为例,如下所示:
#include <unistd.h>
#include <iostream>
#include <fcntl.h>
#include <stdio.h>
using namespace std;int main()
{int fd = -1;if((fd = open("test.txt",O_RDWR|O_CREAT|O_TRUNC)) == -1) //打开并清空外部文件{cout<<"open failed !"<<endl;return -1;}int oldfd = dup(1); //保存标准输出对应的文件表项dup2(fd,1); //重定向标准输出到外部文件test.txt中cout<<"重定向标准输出测试!"<<endl; //重定向测试dup2(oldfd,1); //将重定向后的文件描述符1再次重定向到一开始保存的标准输出对应的文件表项中cout<<"重定向标准输出恢复测试!"<<endl; //重定向恢复测试close(fd);close(oldfd);return 0;
}
根据运行结果可知,在第一次重定向后,cout输出信息是输出到了外部文件中,当再次重定向进行恢复之后,此时的cout就将数据输出到显示器上了,回到了最原始的标准输出。
总结
以上介绍了dup和dup2函数,并且使用dup2函数实现了标准输入、标准输出和标准错误输出的重定向,以及dup和dup2函数共同使用实现重定向恢复。需要注意的是,在调用dup或者dup2函数之后,至少会有两个文件描述符指向同一个文件表项,由于文件表项中含有文件标志(即open时的flag)以及文件偏移等信息,因此这些信息对于这些文件描述符来说都是共享的。
dup、dup2实现文件描述符重定向(标准输入、标准输出、标准错误输出)相关推荐
- Linux:dup/dup2 文件描述符重定向函数(有图有代码有真相!!!)
一.dup/dup2 有时我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接.系统调用dup和dup2能够复制文件描述符.dup返回新的文件文件描述符(没有用的文件描述符最小的编号 ...
- 如何理解Linux shell中的“2>1”(将文件描述2(标准错误输出)的内容重定向到文件描述符1(标准输出))(尼玛>符号竟然不支持搜索,害我搜搜不到,只能搜)
文章目录 前言 有何妙用 如何理解 总结 前言 有时候我们常看到类似这样的脚本调用: ./test.sh > log.txt 2>&1 这里的2>&1是什么意思?该如 ...
- pythonsys标准_python 以标准输出(sys.stdout)为例,看python的标准输入、标准错误输出...
标准输出(sys.stdout)对应的操作就是print(打印)了,标准输入(sys.stdin)则对应input(接收输入)操作,标准错误输出和标准输出类似也是print(打印). python最基 ...
- dup2复制文件描述符
dup2是Linux下用来实现文件描述符复制的api,dup2(fd1, fd2)将会把fd1复制到指定的fd2下,如果fd2是一个已经打开的描述符,dup2会自动的先将其安静的关闭.我们知道Linu ...
- linux 标准输出 复制,使用LINUX dup2 复制文件描述符到标准输出STDOUT_FILENO
7 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 15 16 17 int main(int arg ...
- 【Linux篇】第九篇——基础IO(系统文件IO+文件描述符+重定向+文件系统+软硬链接)
⭐️这篇博客就要开始聊一聊Linux中基础IO相关知识,IO相信大家都不陌生,我们在C/C++中对文件进行读写的操作,也就是文件IO,这篇博客我也会带大家回顾一下.这篇博客还会介绍系统中的文件IO调用 ...
- python标准输入输出用来干什么_python 以标准输出(sys.stdout)为例,看python的标准输入、标准错误输出...
标准输出(sys.stdout)对应的操作就是print(打印)了,标准输入(sys.stdin)则对应input(接收输入)操作,标准错误输出和标准输出类似也是print(打印). python最基 ...
- C++ 标准输入,标准输出,标准错误和标准日志
C++ 标准库提供了一组丰富的输入/输出功能,我们将在后续的章节进行介绍.本章将讨论 C++ 编程中最基本和最常见的 I/O 操作. C++ 的 I/O 发生在流中,流是字节序列.如果字节流是从设备( ...
- 【看表情包学Linux】文件描述符 | 重定向 Redirection | dup2 函数 | 缓冲区的理解 (Cache)
最新文章
- MySQL查询日志介绍
- jQuery-DOM节点插入总结
- MyEclipse使用总结——使用MyEclipse打包带源码的jar包
- Android之如何判断当前是阿拉伯布局的方法
- python中列表如何比较大小_如何比较python中的列表/列表?
- 深度学习(三十七)优化求解系列之(1)简单理解梯度下降
- 金融数据分析之财务分析表要填数据怎么办?(学习理财课程后开发的助手工具)
- CentOS node,npm,cnpm 环境部署
- C++视频和讲义下载地址
- Codeigniter3学习笔记三(创建类库及使用原生类库)
- 电力-101/104规约基础2
- Python修改图片格式
- python中的.nc文件处理 | 03 指定位置的数据切片及可视化
- 如何在html中播放本地视频文件【兼容ie、火狐、谷歌、360浏览器等】
- 英语语言测试学什么软件,开言英语APP,让语言学习在真实情况下进行
- 网易云音乐.uc格式的缓存文件转.mp3
- 关于EFI系统分区(ESP)你应该知道的3件事
- OUC-SE-GROUP09-BLOG1
- 人类幽门螺旋杆菌感染率高
- 数学建模学习笔记01之玻璃的热量散失和方案比较
热门文章
- python安装百度aip_百度Aip人脸识别之python代码
- win32API 开发的音乐播放器
- WebContent vs webapp
- Nvidia MX150安装Tensorflow-GPU版,Pycharm使用Keras
- 云虚拟主机FTP连接不上的解决办法
- 亚马逊Echo将引领新的交互潮流?
- “上下班三个小时”:比收支自由更难实现的,这届成年人的通勤自由
- Android最好用的底部导航栏
- 当用户忠诚度成为一念之间?走进大会开幕日, 了解何以数字优先!
- php调试常用,最常用的8款PHP调试工具,8款调试工具_PHP教程