【问题标题】:How Do I Point a File Handle to STDOUT (or Another File Handle) In Perl?如何在 Perl 中将文件句柄指向 STDOUT(或另一个文件句柄)?
【发布时间】:2016-12-26 10:54:37
【问题描述】:

我想制作一个快速脚本,如果给定文件,则写入文件,如果没有给定文件,则写入标准输出。如果我可以通过将一个新的文件句柄 OUTFILE 指向其中任何一个合适的句柄来启动脚本,那么我这样做会容易得多。

我知道我可以通过在适当的时候将 STDOUT 指向我的输出文件来完成我想要的,但我不想这样做,因为我不想让以后使用该脚本的任何人感到困惑,并想知道为什么他们的打印语句不不行。此外,我仍然希望自己使用偶尔的打印语句进行故障排除,并且永远不希望将此打印到输出文件。

所有这些都说了,我正在寻找的内容类似于:
open( OUTFILE, ($output ? ">$output" : STDOUT );
除了那不起作用。

想法?

【问题讨论】:

    标签: perl


    【解决方案1】:

    一些方法(globs):

    # Requires 2-arg open, unfortunately
    open(OUTPUT, "> ".($output || '-'))  or die;
    

    if ($output) {
       open(OUTPUT, '>', $output) or die;
    } else {
       *OUTPUT = *STDOUT;
    }
    

    if ($output) {
       open(OUTPUT, '>', $output) or die;
    } else {
       open(OUTPUT, '>&', \*STDOUT) or die;
    }
    

    一些方式(词法变量):

    # Requires 2-arg open, unfortunately
    open(my $fh, "> ".($output || '-'))  or die;
    

    my $fh;
    if ($output) {
       open($fh, '>', $output) or die;
    } else {
       $fh = \*STDOUT;
    }
    

    my $fh;
    if ($output) {
       open($fh, '>', $output) or die;
    } else {
       open($fh, '>&', \*STDOUT) or die;
    }
    

    一些方式(其他):

    # Changes the default handle for <print "foo">.
    if ($output) {
       open(OUTPUT, '>', $output) or die;
       select(OUTPUT);
    }
    

    【讨论】:

    • ... } else { open($fh, '&gt;&amp;' . fileno(STDOUT)); }
    【解决方案2】:

    为了读者的利益(因为 OP 已经“开始工作了”):

    Perl 文档“perlopenut”(perldoc perlpentut) 给出了打开指向 STDOUT 的备用文件句柄的示例,但它使用裸字文件句柄和 open 的双 arg 版本,两者都有些古老。 Damian Conway 的书“Perl Best Practices”(以及基于 Damian 的书的 Perl::Critic)强调使用三个 arg 打开和词法文件句柄(my $foo 样式文件句柄)。 chromatic 的“Modern Perl”还简要提到了使用词法文件句柄,并在实用时不鼓励使用裸词。三个 arg open 被认为是一种更安全的形式,因为它限制了壳注射的暴露。虽然这里的具体情况是低风险的,但默认使用三个参数 open 仍然是一个好习惯。

    它需要仔细阅读 Perl 的 open 文档 (perldoc -f open) 中显示的示例,并进行一些解释以注意在打开指向 STDOUT 的重复 filahandle 时正确放置 & 字符时使用open 的三参数版本。有人可能会错误地认为这应该有效:open my $fh, '&gt;', '&amp;STDOUT' or die $!;,主要是因为它看起来像我们习惯看到的一种格​​式(符号名称前的 & 符号)。但这种语法并不是open 需要的,我们习惯于在完全不同的环境中看到它:某些子程序调用。

    使用三个参数open 打开STDOUT 的欺骗的正确方法是将&gt;&amp; 组合到同一个第二个参数中,并保留STDOUT,如下所示:

    use Modern::Perl;
    
    open my $fh, '>&', STDOUT or die "Bleah: $!";
    
    say $fh 'Printing alternate filehandle "$fh" worked.';
    say 'Implicitly printing to "STDOUT" also works.';
    

    或者将STDOUT 传递给open 作为对typeglob 的引用:

    open my $fh, '>&', \*STDOUT or ...
    

    【讨论】:

      【解决方案3】:

      - 是魔法。遵守约定,否则你就违反了最小意外原则。

      use autodie qw(:all);
      use Getopt::Long qw(GetOptions);
      GetOptions(\my %opt, 'out=s') or die 'usage';
      die 'usage' unless $opt{out};
      open my $handle, ">$opt{out}";
      

      perl foo -o blahblah        # opens file blahblah for writing
      perl foo -o -               # goes to STDOUT
      

      相关:Are there reasons to ever use the two-argument form of open(...) in Perl?

      【讨论】:

        【解决方案4】:

        没关系。弄清楚了。扔一个&符号就可以了。完整的工作代码是:
        open( OUTPUT, ( $output ? "&gt;$output" : "&gt;&amp;STDOUT" ) );

        【讨论】:

        • 让这个答案很棒的是解释它是如何工作的。
        【解决方案5】:

        您始终可以将常规输出写入STDOUT,将调试语句写入STDERR。然后,如果用户希望将输出转到文件,他们只需将 STDOUT 重定向到命令行上的文件。

        【讨论】:

        • 就像我说的,我知道我可以将文件重定向到 STDOUT,但这确实是我宁愿避免做的事情,因为当其他人这样做时我发现它令人困惑和烦人,我必须处理后面的代码。它将作为最后的努力,但我更愿意将不同的文件句柄指向输出文件或 STDOUT,因为我认为这会更干净。
        • @Eli - 您不必在代码中执行此操作。只需编写脚本以始终打印到 STDOUT。当用户正常运行脚本时,他们会在屏幕上看到输出。但他们也可以执行scriptname.pl &gt; filename.txt,在这种情况下,脚本将运行并且输出将在文件filename.txt 中结束。 STDERR 上的任何调试代码仍将出现在控制台上。这是大多数 *nix 实用程序和脚本的工作方式。
        • @Eli - 这是最简单的方法。您的脚本中没有任何代码知道不同的输出路径。您在代码中所做的只是写入 STDOUT。用户在使用脚本时决定是否要在控制台中查看输出,或者将其放入文件中,甚至将其通过管道传输到另一个命令的 STDIN。
        猜你喜欢
        • 2013-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-13
        • 2012-08-14
        • 1970-01-01
        相关资源
        最近更新 更多