目录

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实现文件描述符重定向(标准输入、标准输出、标准错误输出)相关推荐

  1. Linux:dup/dup2 文件描述符重定向函数(有图有代码有真相!!!)

    一.dup/dup2 有时我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接.系统调用dup和dup2能够复制文件描述符.dup返回新的文件文件描述符(没有用的文件描述符最小的编号 ...

  2. 如何理解Linux shell中的“2>1”(将文件描述2(标准错误输出)的内容重定向到文件描述符1(标准输出))(尼玛>符号竟然不支持搜索,害我搜搜不到,只能搜)

    文章目录 前言 有何妙用 如何理解 总结 前言 有时候我们常看到类似这样的脚本调用: ./test.sh > log.txt 2>&1 这里的2>&1是什么意思?该如 ...

  3. pythonsys标准_python 以标准输出(sys.stdout)为例,看python的标准输入、标准错误输出...

    标准输出(sys.stdout)对应的操作就是print(打印)了,标准输入(sys.stdin)则对应input(接收输入)操作,标准错误输出和标准输出类似也是print(打印). python最基 ...

  4. dup2复制文件描述符

    dup2是Linux下用来实现文件描述符复制的api,dup2(fd1, fd2)将会把fd1复制到指定的fd2下,如果fd2是一个已经打开的描述符,dup2会自动的先将其安静的关闭.我们知道Linu ...

  5. 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 ...

  6. 【Linux篇】第九篇——基础IO(系统文件IO+文件描述符+重定向+文件系统+软硬链接)

    ⭐️这篇博客就要开始聊一聊Linux中基础IO相关知识,IO相信大家都不陌生,我们在C/C++中对文件进行读写的操作,也就是文件IO,这篇博客我也会带大家回顾一下.这篇博客还会介绍系统中的文件IO调用 ...

  7. python标准输入输出用来干什么_python 以标准输出(sys.stdout)为例,看python的标准输入、标准错误输出...

    标准输出(sys.stdout)对应的操作就是print(打印)了,标准输入(sys.stdin)则对应input(接收输入)操作,标准错误输出和标准输出类似也是print(打印). python最基 ...

  8. C++ 标准输入,标准输出,标准错误和标准日志

    C++ 标准库提供了一组丰富的输入/输出功能,我们将在后续的章节进行介绍.本章将讨论 C++ 编程中最基本和最常见的 I/O 操作. C++ 的 I/O 发生在流中,流是字节序列.如果字节流是从设备( ...

  9. 【看表情包学Linux】文件描述符 | 重定向 Redirection | dup2 函数 | 缓冲区的理解 (Cache)

最新文章

  1. MySQL查询日志介绍
  2. jQuery-DOM节点插入总结
  3. MyEclipse使用总结——使用MyEclipse打包带源码的jar包
  4. Android之如何判断当前是阿拉伯布局的方法
  5. python中列表如何比较大小_如何比较python中的列表/列表?
  6. 深度学习(三十七)优化求解系列之(1)简单理解梯度下降
  7. 金融数据分析之财务分析表要填数据怎么办?(学习理财课程后开发的助手工具)
  8. CentOS node,npm,cnpm 环境部署
  9. C++视频和讲义下载地址
  10. Codeigniter3学习笔记三(创建类库及使用原生类库)
  11. 电力-101/104规约基础2
  12. Python修改图片格式
  13. python中的.nc文件处理 | 03 指定位置的数据切片及可视化
  14. 如何在html中播放本地视频文件【兼容ie、火狐、谷歌、360浏览器等】
  15. 英语语言测试学什么软件,开言英语APP,让语言学习在真实情况下进行
  16. 网易云音乐.uc格式的缓存文件转.mp3
  17. 关于EFI系统分区(ESP)你应该知道的3件事
  18. OUC-SE-GROUP09-BLOG1
  19. 人类幽门螺旋杆菌感染率高
  20. 数学建模学习笔记01之玻璃的热量散失和方案比较

热门文章

  1. python安装百度aip_百度Aip人脸识别之python代码
  2. win32API 开发的音乐播放器
  3. WebContent vs webapp
  4. Nvidia MX150安装Tensorflow-GPU版,Pycharm使用Keras
  5. 云虚拟主机FTP连接不上的解决办法
  6. 亚马逊Echo将引领新的交互潮流?
  7. “上下班三个小时”:比收支自由更难实现的,这届成年人的通勤自由
  8. Android最好用的底部导航栏
  9. 当用户忠诚度成为一念之间?走进大会开幕日, 了解何以数字优先!
  10. php调试常用,最常用的8款PHP调试工具,8款调试工具_PHP教程