如果STDOUT是另一个目的地,如何打印到STDERR?

h q*_*h q 7 unix io perl stdout stderr

我希望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
Run Code Online (Sandbox Code Playgroud)

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

bash $ outerr.pl
Hello standard output!
Run Code Online (Sandbox Code Playgroud)

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

bash $ outerr.pl > /dev/null
Hello standard error
Run Code Online (Sandbox Code Playgroud)

和反过来相似:

bash $ outerr.pl 2> /dev/null
Hello standard output!
Run Code Online (Sandbox Code Playgroud)

如果我将两个/ err重定向到同一个文件,则只显示out:

bash $ outerr.pl > foo.txt 2>&1
bash $ cat foo.txt
Hello standard output!
Run Code Online (Sandbox Code Playgroud)

那么有没有办法评估/确定OUT和ERR是否指向相同的"事物"(描述符?)?

cjm*_*cjm 6

在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]));
Run Code Online (Sandbox Code Playgroud)

但这不适用于Windows,它没有真正的inode数字.它给出了两个误报(认为它们不同时它们是不同的)和假否定(认为它们不相同时它们是相同的).


Ing*_*ngo 5

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

-t STDERR
Run Code Online (Sandbox Code Playgroud)

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

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

因此,如果

-t STDERR && ! (-t STDOUT) || -t STDOUT && !(-t STDERR)
Run Code Online (Sandbox Code Playgroud)

或更短

-t STDOUT ^ -t STDERR  # thanks to @mob
Run Code Online (Sandbox Code Playgroud)

你知道你没事。

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

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

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

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