9

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

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

#!/usr/bin/perl

use strict;
use warnings;

print STDOUT "Hello standard output!\n";
print STDERR "Hello standard error\n" 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 是否指向同一个“事物”(描述符?)?

4

2 回答 2

11

在 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]));

但这不适用于没有实际 inode 编号的 Windows。它给出了误报(认为它们不不同时它们是不同的)和误报(认为它们不同时它们是相同的)。

于 2013-03-14T19:13:41.473 回答
4

你可以(几乎)用 -t 做到这一点:

-t STDERR

如果它是终端则为真,对于 STDOUT 也是如此。

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

因此,如果

-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 的文件描述符所在的位置,如果它们不相等,则将其中一个重定向>>到非空文件。
  2. 否则,将 42 字节写入文件描述符 2
  3. 查找文件描述符 1 的末尾。如果它比以前多 42,则两者都被重定向到同一个文件的机会很高。如果它没有改变,文件是不同的。如果它被改变了,但不是 42,其他人正在那里写,所有的赌注都被取消了(但是,你不在 Windows 中,所以 stat 方法将起作用)。
于 2013-03-14T18:34:38.603 回答