Linux输入输出重定向,我的代码实证和深度理解

363人浏览   2024-06-08 09:46:37

事先申明,本文在理解Linux输入输出重定向方面,是有点干货的。有别于一般泛泛然的讲解,内容以源码为证,讲的比较实在

Linux的shell命令的高级操作,主要有:

  • 输出重定向,操作符是:>、>>
  • 输入重定向,操作符是:<
  • 管道操作,操作符是(|)

为了从原理上说明管道操作,我们首先得了解文件描述符。关于文件描述符的背景知识,请参看我发过的笔记《我当年的Linux系统编程笔记-文件描述符》,这里不再复述。这里只对文件描述符、标准文件描述符的概念进行一个大致的讲解。

1 文件描述符

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。在windows中,内核记录应用打开的对象,就是handle句柄,也是一个整数值,两者有相似之处。

在Linux中,在编写脚本的时候会频繁使用标准输入( stdin)、标准输出( stdout)和标准错误( stderr)。通过内容过滤将输出重定向到文件是我们平日里的基本任务之一。文件描述符是与某个打开的文件或数据流相关联的整数。文件描述符0、 1以及2是系统预留的,相当于每个进程的全局变量,它们被称为标准文件描述符。

2 Shell 输入重定向"<"的基本概念


命令语法:command < file

对该命令的解释是:将输入重定向到 file。

进一步的解释是:本来需要从键盘stdin获取输入的命令,会转移到文件file读取内容

3 Shell 输入重定向"<"命令的例子

$ cat input.txt

Line1:This is a sample

Line2:123456abc

Line3:Just file!

$ wc -l < input.txt

3 (wc命令统计的行数)

注意:如果命令不能从标准设备(stdin)读取数据,这个命令就不能做输入重定向。举例如下:

$ cat wantToEcho.txt

Line1:This is wantToEcho file

$ echo < wantToEcho.txt

命令“ echo < wantToEcho.txt”不会显示任何数据到命令行上。因为echo命令没有从标准设备(stdin)读取数据的能力,所以不会执行成功。

4 代码实证理解

从程序的观点出发,输入重定向命令“command < file”中,Linux管道机制将操作符<右边的file打开后,输出到stdin。如果command具有从stdin读取数据的动作,就正好接收处理。不符合这种情况的命令,不可以用到输入重定向中来。

怎么证实这个观点呢?还是下面的代码"in-redirect.c"验证最为直接。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_LEN 1024
int main(void)
{
    char read_buf[BUF_LEN +1];
    ssize_t len_read;
    len_read = read(0,read_buf,BUF_LEN);  //这个0,就是stdin

    if ( len_read < 0 )
    {
        perror("perror:file read error ");
        exit(1);
    }

    read_buf[len_read]=0x0;
    printf("read stdin:%s\n",read_buf);
    return 0;
}

编译:gcc in-redirect.c -o in-redirect.out

下面是验证过程:

$ ./in-redirect.out

123456abc789 (此处手工输入)

read stdin:123456abc789 

$ cat input.txt 下面一行是input.txt文件中的内容

Line1:This is a sample

$ ./in-redirect.out < input.txt

read stdin:Line1:This is a sample

in-redirect.out这个程序,会从stdin中读取内容,通过“<”把它重定向成向input.txt中读数据了。因为有了源码示例,所以这个对“<”的理解比较通透

为什么上面的代码“read(0,...);”之前,不用先open打开呢?

这是因为“标准文件描述符”有别于其它“文件描述符”,它们是进程创建就默认打开着的,不用open。你可以理解为0、1、2这三个文件描述符是进程的全局变量。既然是全局变量,拿来就用,可以read,也可以write,就不用open了,因为进程在创建之初,就相当于为你open过了。

5 输入重定向(<)和输出重定向(>、>>)的区别

从系统编程的角度来理解,输出重定向"command > file"就是:command命令输出数据,向stdout或stderr输出(write)数据,Linux Shell把这些数据重新定向(open)输出(write)到file文件中。也就是说:输出重定向就是对stdout或stderr进行重定向

而输入重定向“command < file”,则是把Linux Shell把文件打开(open),writestdio中去,然后command来读取(readstdio的数据并进行处理。也就是说:输入重定向,就是对stdin进行重定向

6 输入重定向和输出重定向混用

其实,只要符合输入输出重定向的语法,内部再符合标准输出输出的stdout、stdin、stderr的读read写write要求,都可以组合使用,不管命令有多么长

比如上面的命令:wc -l < input.txt (统计input.txt的行数),如果我们想把它再输出到一个计数文件cout.txt中,可以这样做

$ cat input.txt

Line1:This is a sample

Line2:123456abc

Line3:Just file!

$ wc -l < input.txt > cout.txt

$ cat cout.txt

3

这两个命令和执行结果的说明:

wc -l < input.txt > cout.txt :wc命令本来要从stdin中读取数据,结果被输入重定向了(<),变成从文件input.txt中去读数据了。输出的结果本来要输出到stdout屏幕,结果被输出重定向了(>),变成了输出到文件cout.txt中了。

相关推荐