五、文件IO——dup 函数
5.1 dup 函数---复制文件描述符
5.1.1 简单cat实现及输入输出重定向
io.c
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <unistd.h> 5 #include "io.h" 6 #include <string.h> 7 #include <errno.h> 8 #include <stdlib.h> 9 #include <stdio.h> 10 #include <fcntl.h> 11 12 13 #define BUFFER_LEN 1024 14 15 /* 文件的读写拷贝 */ 16 void copy(int fdin, int fdout) 17 { 18 char buff[BUFFER_LEN]; 19 ssize_t size; 20 21 // printf("file length: %ld\n", lseek(fdin, 0L, SEEK_END));//将文件定位到文件尾部,偏移量为0L 22 // lseek(fdin, 0L, SEEK_SET);// 定位到文件开头 23 24 while((size = read(fdin, buff, BUFFER_LEN)) > 0) { //从 fdin 中读取 BUFFER_LEN 个字节存放入 buff 中 25 // printf("current: %ld\n", lseek(fdin, 0L, SEEK_CUR)); 26 27 if(write(fdout, buff, size) != size) { 28 fprintf(stderr, "write error: %s\n", strerror(errno)); 29 exit(1); 30 } 31 } 32 if(size < 0) { 33 fprintf(stderr, "read error:%s\n", strerror(errno)); 34 exit(1); // 相当于 return 1; 35 } 36 }
cat.c
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <unistd.h> 5 #include "io.h" 6 #include <string.h> 7 #include <errno.h> 8 #include <stdlib.h> 9 #include <stdio.h> 10 #include <fcntl.h> 11 12 int main(int argc, const char *argv[]) 13 { 14 int fd_in = STDIN_FILENO; 15 int fd_out = STDOUT_FILENO; 16 int i; 17 18 for(i = 1; i < argc; i++) { 19 fd_in = open(argv[i], O_RDONLY); 20 if(fd_in < 0) { 21 perror("open error"); 22 continue; 23 } 24 25 copy(fd_in, fd_out); 26 close(fd_in); 27 } 28 29 if(argc == 1) 30 copy(fd_in, fd_out); 31 32 return 0; 33 }
编译:gcc -o bin/cat -Iinclude src/io.c src/cat.c
调试:
cat 的作用就输入和输出的重定向功能,而 dup 和 dup2 就是用来完成此功能的。
5.1.2 dup 和 dup2 函数说明
1 #include<unistd.h> 2 int dup (int oldfd); 3 int dup2(int odlfd,int newfd);
- 函数说明:
- dup() 用来复制参数 oldfd 所指的文件描述符,并将它返回。此新的文件描述符和参数oldfd指的是同一个文件,共享所有的锁定、读写位置和各项权限或旗标。例如,当利用 lseek() 对某个文件描述符作用时,另一个文件描述词的读写位置也会随着改变。不过,文件描述符之间并不共享 close-on-exec 旗标。
- dup2() 用来复制参数 oldfd 所指的文件描述符,并将它拷贝至参数 newfd 后一块返回。若参数 newfd 为一已打开的文件描述词,则 newfd 所指的文件会先被关闭。如若 oldfd 等于 newfd,则dup2 返回 newfd,而不关闭它。dup2() 所复制的文件描述符,与原来的文件描述符共享各种文件状态,详情可参考 dup() 。
- 参数:
- oldfd:原先的文件描述符
- newfd:新的文件描述符
- 返回值:当复制成功时,则返回最小及尚未使用的文件描述符。若有错误则返回-1,errno会存放错误代码。
- 附加说明:
- dup2() 相当于调用 fcntl(oldfd,F_DUPFD,newfd);请参考 fcntl()
- 在进程间通信时可用来改变进程的标准输入和标准输出。
文件描述符的赋值是文件描述符表结构体成员中指针的复制。
dup2(fd_in, STDIN_FILENO),相当于就是将 fd_in 文件描述符中的指向文件表项的指针赋值给 STDIN_FILENO 中的指向标准输入的指针,此时,STDIN_FILENO 中的指针指向了fd_in 中的指针指向的文件表项(地址一样了),最终指向了读取的文件。
则此时 dup1 中 STDIN_FILENO 不再是从标准输入读取,而是从 fd_in 中去读取。这种就是重定向
5.1.3 实例
将+ 和 - 改为输入输出重定向
mcat.c
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <unistd.h> 5 #include "io.h" 6 #include <string.h> 7 #include <errno.h> 8 #include <stdlib.h> 9 #include <stdio.h> 10 #include <fcntl.h> 11 12 /* 13 * bin/mcat + append.txt(+ 为输入重定向) 14 * bin/mcat - append.txt(- 为输出重定向) 15 */ 16 int main(int argc, const char *argv[]) 17 { 18 int fd_in; 19 int fd_out; 20 int i; 21 int flag = 0; 22 23 for(i = 1; i < argc; i++) { 24 if(!strcmp("+", argv[i])) { 25 fd_in = open(argv[++i], O_RDONLY); 26 if(fd_in < 0) { 27 perror("open error"); 28 exit(1); 29 } 30 31 //将标准输入重定向到文件 32 if(dup2(fd_in, STDIN_FILENO) != STDIN_FILENO) { 33 perror("dup2 error"); 34 exit(1); 35 } 36 37 close(fd_in); 38 } else if(!strcmp("-", argv[i])) { 39 fd_out = open(argv[++i], O_WRONLY | O_CREAT | O_TRUNC, 0777); 40 if(fd_out < 0) { 41 perror("open error"); 42 exit(1); 43 } 44 45 //将标准输出重定向到文件 46 if(dup2(fd_out, STDOUT_FILENO) != STDOUT_FILENO) { 47 perror("dup2 error"); 48 exit(1); 49 } 50 51 close(fd_out); 52 53 } else { 54 flag = 1; 55 fd_in = open(argv[i], O_RDONLY); 56 if(fd_in < 0) { 57 perror("open error"); 58 exit(1); 59 } 60 61 if(dup2(fd_in, STDIN_FILENO) != STDIN_FILENO) { 62 perror("dup2 error"); 63 exit(1); 64 } 65 66 copy(STDIN_FILENO, STDOUT_FILENO); 67 close(fd_in); 68 } 69 } 70 71 if(!flag) { 72 copy(STDIN_FILENO, STDOUT_FILENO); 73 } 74 75 return 0; 76 }
编译调试如下: