如何仅当STDOUT是不同的目的地时才打印到STDERR



我希望Perl只在STDOUT不相同的情况下写入STDERR。例如,如果STDOUT和STDERR都将输出重定向到终端,那么我不希望打印STDERR。

考虑以下示例(outer .pl):

#!/usr/bin/perl
use strict;
use warnings;
print STDOUT "Hello standard output!n";
print STDERR "Hello standard errorn" if ($someMagicalFlag);
exit 0

现在考虑这个(这是我想要实现的):

bash $ outerr.pl
Hello standard output!

但是,如果我重定向到一个文件,我希望得到:

bash $ outerr.pl > /dev/null
Hello standard error

,反过来也一样:

bash $ outerr.pl 2> /dev/null
Hello standard output!

如果我将out/err重定向到相同的文件,那么应该只显示out:

bash $ outerr.pl > foo.txt 2>&1
bash $ cat foo.txt
Hello standard output!

那么是否有一种方法来评估/确定OUT和ERR是否指向相同的"东西"(描述符?)?

在unix风格的系统上,您应该能够做到:

my @stat_err = stat STDERR;
my @stat_out = stat STDOUT;
my $stderr_is_not_stdout = (($stat_err[0] != $stat_out[0]) ||
                            ($stat_err[1] != $stat_out[1]));

但是这在Windows上不起作用,因为Windows没有真正的索引号。它同时给出假阳性(当它们不相同时认为它们不同)和假阴性(当它们不相同时认为它们相同)。

使用-t:

几乎可以做到这一点
-t STDERR
如果

是终端,则

为真,STDOUT也是如此。

这仍然不会告诉你是哪个终端,如果你重定向到同一个文件,你仍然可以得到两个终端。

因此,if

-t STDERR && ! (-t STDOUT) || -t STDOUT && !(-t STDERR)

还是短

-t STDOUT ^ -t STDERR  # thanks to @mob

你知道你很好。

编辑:对于STDERR和STDOUT都是常规文件的解决方案:

Tom Christianson建议启动并比较dev和ino字段。这在UNIX中可以工作,但是,正如@cjm指出的,在Windows中不行。

如果您可以保证没有其他程序将写入文件,则可以在Windows和UNIX中执行以下操作:

  1. 检查STDOUT和STDERR的文件描述符的位置,如果它们是不相等的,你把其中一个用>>重定向到a非空的文件。
  2. 否则,写入42字节到文件描述符2
  3. 查找到文件描述符1的末尾。如果它比以前多42,则很有可能两者都被重定向到同一个文件。如果未更改,则表示文件不同。如果它被改变了,但不是42,其他人在那里写,所有的赌注都取消了(但是,你不是在Windows中,所以stat方法将工作)。

最新更新