五、Linux系统IO编程—dup与dup2函数

一、cat输入输出的重定向

1. cat命令
  • cat 命令用于连接文件并打印到标准输出设备上。

语法格式

  • cat [-AbeEnstTuv] [–help] [–version] fileName

参数说明:

  • -n 或 --number:由 1 开始对所有输出的行数编号。

  • -b 或 --number-nonblank:和 -n 相似,只不过对于空白行不编号。

  • -s 或 --squeeze-blank:当遇到有连续两行以上的空白行,就代换为一行的空白行。

  • -v 或 --show-nonprinting:使用 ^ 和 M- 符号,除了 LFD 和 TAB 之外。

  • -E 或 --show-ends : 在每行结束处显示 $。

  • -T 或 --show-tabs: 将 TAB 字符显示为 ^I。

  • -A, --show-all:等价于 -vET。

  • -e:等价于"-vE"选项;

  • -t:等价于"-vT"选项;

实例:

把 textfile1 的文档内容加上行号后输入 textfile2 这个文档里:

cat -n textfile1 > textfile2

把 textfile1 和 textfile2 的文档内容加上行号(空白行不加)之后将内容附加到 textfile3 文档里:

cat -b textfile1 textfile2 >> textfile3

清空 /etc/test.txt 文档内容:

cat /dev/null > /etc/test.txt

cat 也可以用来制作镜像文件。例如要制作软盘的镜像文件,将软盘放好后输入:

cat /dev/fd0 > OUTFILE

相反的,如果想把 image file 写到软盘,输入:

cat IMG_FILE > /dev/fd0

注:
1. OUTFILE 指输出的镜像文件名。
2. IMG_FILE 指镜像文件。
3. 若从镜像文件写回 device 时,device 容量需与相当。
4. 通常用制作开机磁片。
在这里插入图片描述

2. more命令

Linux more 命令类似 cat ,不过会以一页一页的形式显示,更方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 b 键就会往回(back)一页显示,而且还有搜寻字串的功能(与 vi 相似),使用中的说明文件,请按 h 。

3. 案例

编程实现cat命令的输入输出重定向。
io.o

#ifndef __IO_H__
#define __IO_H__

extern void copy(int fdin,int fdout);

#endif

io.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "io.h"

/*定义缓存的大小*/
#define BUFFER_LEN 1024
/*复制输入输出文件*/
void copy(int fdin,int fdout)
{
	char buffer[BUFFER_LEN];
	size_t size;
	while((size=read(fdin,buffer,BUFFER_LEN))>0)
	{
		if(write(fdout,buffer,size)!=size)
		{
			fprintf(stderr,"write error:%s\n",strerror(errno));
			exit(1);
		}

	}
	if(size<0)
	{
		fprintf(stderr,"read error:%s\n",strerror(errno));
		exit(1);
	}	
}

cat.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "io.h"

int main(int argc, char *argv[])
{
	int fd_in=STDIN_FILENO;//标准的输入函数
	int fd_out =STDOUT_FILENO;//标准的输出函数

	int i;

	for(i=1;i<argc;i++)
	{
		fd_in=open(argv[i],O_RDONLY);
		if(fd_in<0)
		{
			perror("open error\n");
			continue;
		}
		copy(fd_in,fd_out);
		close(fd_in);
	}
	if(argc==1)
	{
		copy(fd_in,fd_out);
	}
	return 0;
}

cat 的作用就输入和输出的重定向功能,而 dup 和 dup2 就是用来完成此功能的。在这里插入图片描述

二、文件描述符在内核的数据结构

在具体说dup/dup2之前,我认为有必要先了解一下文件描述符在内核中的形态。一个进程在此存在期间,会有一些文件被打开,从而会返回一些文件描述符,从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2),0与进程的标准输入相关联,1与进程的标准输出相关联,2与进程的标准错误输出相关联,一个进程当前有哪些打开的文件描述符可以通过/proc/进程ID/fd目录查看。 
在这里插入图片描述
文件表中包含:文件状态标志、当前文件偏移量、v节点指针,这些不是本文讨论的重点,我们只需要知道每个打开的文件描述符(fd标志)在进程表中都有自己的文件表项,由文件指针指向。

三、dup()与dup2()函数

Linux 中进行文件描述符的重定向可以使用两个函数:dup函数和dup2函数,其中还有一个dup3函数,但是这个是Linux后面版本添加的,不是所有的Linux内核都支持,在这里不讨论。

在Linux系统中约定文件描述符 0、1、2 对应标准输入、标准输出、标准错误,默认开启。

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
  • 函数说明:

    • dup() 用来复制参数 oldfd 所指的文件描述符,并将它返回。此新的文件描述符和参数oldfd指的是同一个文件,共享所有的锁定、读写位置和各项权限或旗标。例如,当利用 lseek() 对某个文件描述符作用时,另一个文件描述词的读写位置也会随着改变。不过,文件描述符之间并不共享 close-on-exec 旗标。
    • dup2() 用来复制参数 oldfd 所指的文件描述符,并将它拷贝至参数 newfd 后一块返回。若参数 newfd 为一已打开的文件描述词,则 newfd 所指的文件会先被关闭。如若 oldfd 等于 newfd,则dup2 返回 newfd,而不关闭它。dup2() 所复制的文件描述符,与原来的文件描述符共享各种文件状态,详情可参考 dup() 。
  • 参数:

  • oldfd:原先的文件描述符

  • newfd:新的文件描述符

  • 返回值:当复制成功时,则返回最小及尚未使用的文件描述符。若有错误则返回 -1,errno会存放错误代码。

  • 附加说明:

    • 实际上,调用dup(oldfd)等效于,fcntl(oldfd, F_DUPFD, 0)。
    • dup2(oldfd, newfd)等效于,close(oldfd);fcntl(oldfd, F_DUPFD, newfd);请参考 fcntl() 在进程间通信时可用来改变进程的标准输入和标准输出。

文件描述符的赋值是文件描述符表结构体成员中指针的复制。
在这里插入图片描述
dup2(fd_in, STDIN_FILENO),相当于就是将 fd_in 文件描述符中的指向文件表项的指针赋值给 STDIN_FILENO 中的指向标准输入的指针,此时,STDIN_FILENO 中的指针指向了fd_in 中的指针指向的文件表项(地址一样了),最终指向了读取的文件。
则此时 dup2 中 STDIN_FILENO 不再是从标准输入读取,而是从 fd_in 中去读取。这种就是重定向。

案例

文件描述符的输入输出重定向。将+ 和 - 改为输入输出重定向。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "io.h"

int main(int argc, const char *argv[])
{
	int fd_in;
	int fd_out;
	int i;
    int flag=0;
	for(i=1;i<argc;i++)
	{
		/*输入重定向处理*/
		if(!strcmp("+",argv[i]))
		{
			fd_in=open(argv[++i],O_RDONLY);
			if(fd_in<0)
			{
				perror("open error");
				exit(1);
			}
			/*使用dup2复制文件描述符到标准的输入设备*/
			if(dup2(fd_in,STDIN_FILENO)!=STDIN_FILENO)
			{
				perror("dup2 error");
				exit(1);
			}
			close(fd_in);
		}

		/*输出重定向处理*/
		else if(!strcmp("-",argv[i]))
		{	
			fd_out=open(argv[++i],O_WRONLY|O_CREAT|O_TRUNC,0777);
			if(fd_out<0)
			{
				perror("open error");
				exit(1);
			}
			/*使用dup2复制文件描述符到标准的输出设备*/
			if(dup2(fd_out,STDOUT_FILENO)!=STDOUT_FILENO)
			{
				perror("dup2 error");
				exit(1);
			}
			close(fd_out);
		}
		else 
		{   
			flag=1;
			fd_in=open(argv[i],O_RDONLY);
			if(fd_in<0)
			{
				perror("open error");
				exit(1);
			}
			/*把输入的文件描述符复制到标准的输入设备上*/
			if((dup2(fd_in,STDIN_FILENO))!=STDIN_FILENO)
			{
				perror("dup2 error");
				exit(1);
			}
			copy(STDIN_FILENO,STDOUT_FILENO);
			close(fd_in);
		}

	}
	if(!flag)
	{
		copy(STDIN_FILENO,STDOUT_FILENO);
	}
	return 0;
}

运行结果如下图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值