How do I print to STDERR only if STDOUT is another destination?

I would like Perl to write to STDERR only if STDOUT is not the same. For example, if both STDOUT and STDERR redirected output to a terminal, then I do not want to print STDERR.

Consider the following example (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 

Now consider this (which I would like to achieve):

 bash $ outerr.pl Hello standard output! 

However, if I redirect to a file, I would like to get:

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

and similarly:

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

If I redirect both / err options to the same file, then only the output should be displayed:

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

So, is there a way to evaluate / determine whether OUT and ERR are and point to the same β€œthing” (handle?)?

+6
source share
2 answers

On Unix-style systems, you can:

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

But this will not work on Windows, which does not have real inode numbers. He gives both false positives (thinks they are different if they are not) and false negatives (thinks they are the same when they are not).

+5
source

You can do this (almost) with -t:

 -t STDERR 

will be true if it is a terminal, as well as for STDOUT.

It still won’t tell you which terminal, and if you are redirected to the same file, you can still get both.

Therefore, if

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

or shorter

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

you know that you're okay.

EDIT: Solutions for when STDERR and STDOUT are regular files:

Tom Christianson proposed a stat and compare dev and ino fields. This will work on UNIX, but as @cjm noted, not on Windows.

If you can guarantee that no other program will write to a file, you can do the following on both Windows and UNIX:

  • check the position of the file descriptors for STDOUT and STDERR, if they are not equal, you redirected one of them with >> to a non-empty file.
  • Otherwise, write 42 bytes to file descriptor 2
  • Look for the end of file descriptor 1. If it is larger than before, the likelihood that both of them are redirected to the same file. If it has not changed, the files are different. If it is changed, but not by 42, someone writes there, all bets are disabled (but then you are not on Windows, so the stat method will work).
+4
source

All Articles