dup2 | 考得一首好古

这两天在看 《Unix 环境高级编程》,学到了不少好姿势,还意外地考了一次古。

dup & dup2

关于这两个系统调用,Unix 圣经里已经介绍得很清楚了, man dup可以看用法。

1
2
3
#include <unistd.h>
int dup (int filedes);
int dup2(int filedes, int filedes2);

两函数的返回:若成功为新的文件描述符,若出错为-1,返回的新文件描述符与参数 filedes 共享同一个文件表项。

dup 返回的新文件描述符一定是当前可用文件描述符中的最小数值。
dup2 则可以用 filedes2 参数指定新描述符的数值。如果 filedes2 已经打开,则先将其关闭。如若 filedes 等于 filedes2 ,则返回 filedes2 ,而不关闭它。

有关这部分的知识点我就不啰嗦了,放截图:

这两款调用非常给力,它们经常用来重定向进程的 stdin,stdout.

重定向好似抗战片中,我铁道游击队阻击日本鬼子,将铁轨移到别的路上:或南辕北辙,转向一条错误的道路 fd;或干脆把铁轨坠下深谷 /dev/null

但是,后来问题来了:

你为了让自己干活方便,把管子移到别处了,你干完活后,总不能留下一地鸡毛就走吧,为了不影响后来人,得把现场恢复。

看了下前辈们的血泪史,偶遇一个11年前的好贴 《使用dup2重定向了标准输出后,使用什么方法恢复对终端的输出??》 解决了心中疑惑。

Demo

来来来,搞个 demo 验证下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h>;
#include <unistd.h>;
#include <stdlib.h>;
#include <fcntl.h>
#include <sys/types.h>;
#include <sys/stat.h>;
#include <string.h>;
#include <strings.h>;

#define TESTSTR "Hello dup2\n"

int main ()
{
int fd3;
int s_fd;
int n_fd;
int r_fd;

fd3 = open ("testdup2.dat", O_WRONLY | O_CREAT| O_TRUNC, 0666);
printf ("after open, fds:%d\n", fd3);
if (fd3 < 0) {
printf ("open error\n");
exit (-1);
}

// 复制标准输出描述符
s_fd = dup (STDOUT_FILENO);

printf ("after dup, s_fd:%d\n", s_fd);
if (s_fd < 0) {
printf ("err in dup\n");
exit (-1);
}

// 重定向标准输出到文件
n_fd = dup2 (fd3, STDOUT_FILENO);

// 写入testdup2.dat中
printf ("after dup2, n_fd:%d\n", n_fd);

if (n_fd < 0) {
printf("err in dup2\n");
exit(-1);
}

// 写入testdup2.dat中
write (STDOUT_FILENO, TESTSTR, strlen(TESTSTR));

// 恢复标准输出
r_fd = dup2 (s_fd, STDOUT_FILENO);
printf ("after restore, r_fd:%d\n", r_fd);
if (r_fd < 0) {
printf("err in dup2\n");
}

// 输出到屏幕上
write (STDOUT_FILENO, TESTSTR, strlen(TESTSTR));

return 0;
}

编译,运行:

1
2
3
4
5
6
7
8
9
hxz@pc0170:~/workspace/c++$ gcc -o dup dup.c
hxz@pc0170:~/workspace/c++$ ./dup
after open, fds:3
after dup, s_fd:4
after restore, r_fd:1
Hello dup2
hxz@pc0170:~/workspace/c++$ cat testdup2.dat
after dup2, n_fd:1
Hello dup2

归纳一下,利用 dup/dup2 处理 i/o的大致流程:

1
2
3
4
5
6
7
8
9
10
fd2 = dup(STDOUT_FILENO); //fd2表示stdout

fd = open(filename, O_WRONLY|O_CREAT, fd_mode);
dup2(fd, STDOUT_FILENO); //把输出重定向到新打开的文件fd

//all stdout content will be redirect to fd

close(fd);

dup2(fd2, STDOUT_FILENO); //恢复stdout

小明在 2016 年了翻出了 2004 的技术帖,今天真是考得一首好古啊~蛤蛤

彦祖老师 wechat