перенаправление stderr влияет на вывод "open FH, '- |', $ command". Почему и как это предотвратить? - PullRequest
3 голосов
/ 02 апреля 2012

Я хочу получить размер текущего терминала, т.е. терминала, в котором запущен мой perl-скрипт. Следующее должно делать эту работу:

#!/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";

Без последней, но одной строки кода это работаеткак это должно.Но с этой строкой я всегда получаю 24 x 80, поэтому создается впечатление, что внутри создается новая оболочка и возвращается ее размер.Это просто мое предположение.Так что же на самом деле происходит и как мне получить и перенаправление STDERR и правильный размер?

1 Ответ

3 голосов
/ 02 апреля 2012

Чтобы tput идентифицировал размер терминала, один из файловых дескрипторов для ( stdin ,) stdout или stderr должен быть открыт и подключен к терминалу.

В конструкции трубы stdout подключается к трубе; если вы перенаправляете stderr в файл, то tput возвращает размер по умолчанию, поскольку он не анализирует stdin.

Итак, tput не имеет терминала для работы; он возвращает размер по умолчанию 24x80.


Вы можете обойти эту проблему, добавив 2>/dev/tty к командам tput:

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

Похоже, что tput вообще не смотрит на stdin. (Перенаправление stdout на терминал может привести к потере конвейерного механизма, который Perl использует для чтения информации.)


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

Я добавил диагностическую распечатку в функцию, чтобы убедиться, что она считывает вывод с tput, и это было. Я добавил эти строки в функцию:

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;

и вывод был:

   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 

При запуске в командной строке выдается:

$ 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
$

Итак, стандартный ввод tput все еще был терминалом, но tput не смотрел на это. Следовательно, tput, должно быть, смотрел на stderr (неясно, пробовал ли он stdout, но это была труба), а не на stdin. fstat - это команда домашнего приготовления, по духу похожая на stat, но она имеет другой формат вывода.

...