通过STDIN重定向的输入无法正常工作

时间:2018-07-25 19:55:29

标签: perl io-redirection

我写了一些代码,以为我正在测试是否有STDIN。但是代码的工作与我的预期相反。

这是我编写的代码,我称之为zit

#!/usr/bin/perl
use strict 'vars';

my @a = @ARGV ? @ARGV : "EMPTY";
printf "  command line arguments: \"%s\" as expected\n", @a;
if ( -t STDIN )
  {
    print "  ( -t STDIN ) returns TRUE\n";
  }
else
  {
    print "  ( -t STDIN ) returns FALSE\n";
    print "  But, I can iterate over <STDIN>!  Huh?? Behold:\n";
  }

这就是我的想法:

  • zit <(echo a;echo b)将导致( -t STDIN )FALSE

  • zit < <(echo a;echo b)将导致( -t STDIN )TRUE

因此,为了弄清楚到底发生了什么,我通过添加一个if-then循环来修改了while代码(根据我的理解,我把它放在应该认为会产生问题的地方)。

#!/usr/bin/perl
use strict 'vars';

my @a = @ARGV ? @ARGV : "EMPTY";
printf "  command line arguments: \"%s\" as expected\n", @a;

if ( -t STDIN )
  {
    print "  ( -t STDIN ) returns TRUE\n";
  }
else
  {
    print "  ( -t STDIN ) returns FALSE\n";
    print "  But, I can iterate over <STDIN>!  Huh?? Behold:\n";
    while ( <STDIN> )
      {
        print ">> $_";
      }
  }

这是此代码的输出

zit <(echo a;echo b)具有以下输出

  command line arguments: "/dev/fd/63" as expected
  ( -t STDIN ) returns TRUE

zit < <(echo a;echo b)具有以下输出

  command line arguments: "EMPTY" as expected
  ( -t STDIN ) returns FALSE
  But, I can iterate over <STDIN>!  Huh?? Behold:
>> a
>> b

我对此感到非常困惑。这不是我想的那样。如果( -t STDIN )为假,为什么while循环起作用?

有人可以解释这里发生了什么吗?

为了避免混淆,我从前重新编辑了这篇文章。

1 个答案:

答案 0 :(得分:4)

use strict 'vars';

您为什么要这样做?您实际上应该只use strict;并启用所有三个限制,而不仅仅是vars。您还应该use warnings;


如果您选中perldoc -f -t,则会看到文档中的内容:

  

-t Filehandle is opened to a tty.

换句话说,它不是在测试是否有STDIN(始终有STDIN(除非您的父进程是顽皮的,并且在启动之前将其关闭)),而是在测试STDIN是否引用了终端(缩写“ tty”)最初指的是teletype,但是当它们被text terminals和后来的terminal emulators(例如xterm)替换时,名称就卡住了。用C术语表示,它对应于对isatty的调用。

在第一个示例中,您有zit <(echo a;echo b)<( ... )表示法使bash将其STDOUT重定向到管道来运行指定的命令。然后,它将<( ... )替换为引用管道(管道的读取端)的文件名。

具体地说,它运行zit /dev/fd/63(其中文件描述符63指向管道的读取端,其写入端由echo a; echo b馈送)。 STDIN没有任何反应,因此zit从shell继承了其标准流,因此STDIN引用了一个终端,这就是-t STDIN返回true的原因。

在第二个示例中,您有zit < <(echo a;echo b)。这非常相似,但是现在生成的命令为zit < /dev/fd/63< FILE表示外壳程序打开指定的文件以读取并将STDIN重定向到该文件。

因此,它在STDIN连接到管道(另一端由zit供电)下运行echo a; echo b(没有任何命令行参数)。这实际上与( echo a; echo b ) | zit相同。这里zit的STDIN是指管道,而不是终端,因此-t STDIN返回false。当然,您仍然可以从中读取内容:您可以读取的大多数内容都不是终端,例如普通文件,管道或套接字。