Redirecting stderr affects the output of "open FH, '- |', $ command". Why and how to prevent?

I want to get the size of the current terminal i.e. my perl script terminal works. The following should do the job:

#!/usr/bin/perl

sub getTerminalSize {
    my @dimensions = (24,80);
    open( my $OH, '-|', "/usr/bin/tput lines ; /usr/bin/tput cols" )
        || return @dimensions;
    chomp(@dimensions = <$OH>);
    close($OH);
    return @dimensions;
}

open (STDERR, ">>bla.log") or die "can not create logfile";
print "Dimensions of your terminal: ". (join " x " , getTerminalSize()) ."\n";

      

Without the last but one line of code, it works as it should. But with this line I always get 24 x 80, so it seems like a new shell is internally created and the size of that shell is returned. This is just my guess. So what's really going on and how do I get both the STDERR redirect and the correct size?

+3


source to share


1 answer


To tput

determine the size of the terminal, one of the file descriptors for ( stdin ) stdout or stderr must be open and connected to the terminal.

In the construction, the pipe is stdout

connected to the pipe; if you redirect stderr

to a file then tput

returns the default size because it doesn't parse stdin

.

So it tput

doesn't have a terminal to work with; it returns the default size 24x80.




You can work around the problem by adding 2>/dev/tty

to the commands tput

:

open my $OH, '-|', "/usr/bin/tput lines 2>/dev/tty; /usr/bin/tput cols 2>/dev/tty"
    or return @dimensions;

      

It seems that tput

does not look at stdin

. (Redirecting stdout

to a terminal will win over the mechanism Perl uses to read the information, of course.)




$ tput lines </dev/null
65
$ tput lines </dev/null 2>/dev/null
65
$ x=$(tput lines </dev/null 2>/dev/null)
$ echo $x
24
$

      

I added a diagnostic printout to the function to make sure it reads the output from tput

and it did. I added these lines to the function:

open( my $OH, '-|', "fstat /dev/fd/0 /dev/fd/1 /dev/fd/2 /dev/null")
    or die "horribly";
my(@data) = <$OH>;
close($OH);
print @data;

      

and the output is:

   Mode  Inode Links   UID   GID     Size    Modtime       Dev      RDev File
0020620    623     1   503     4        0 1333369875 334992488 268435457 /dev/fd/0
0010660 590945904     0   503    20        0 1333369875 334992488         0 /dev/fd/1
0100644 111429666     1   503    20        0 1333369875 334992488         0 /dev/fd/2
0020666    304     1     0     0        0 1333359963 334992488  50331650 /dev/null 

      

When run on command line, the output is:

$ fstat /dev/fd/[012] /dev/null
   Mode  Inode Links   UID   GID     Size    Modtime       Dev      RDev File
0020620    623     1   503     4        0 1333370018 334992488 268435457 /dev/fd/0
0020620    623     1   503     4        0 1333370018 334992488 268435457 /dev/fd/1
0020620    623     1   503     4        0 1333370018 334992488 268435457 /dev/fd/2
0020666    304     1     0     0        0 1333359963 334992488  50331650 /dev/null
$

      

So the standard input tput

remained the terminal, but tput

didn't look at it. So tput

had to look at stderr

(unclear if he tried it stdout

, but it was a channel) and not in stdin

. fstat

is a home-brew command, similar in spirit to stat

but with a different output format.

+3


source







All Articles