一、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;
}
运行结果如下图: