Каков наилучший способ определить, содержит ли скаляр дескриптор файла? - PullRequest
29 голосов
/ 09 июля 2010

Я пытаюсь определить, содержит ли данный скаляр дескриптор файла.Он мог быть передан мне из файлового дескриптора голого слова (т. Е. \*FH), лексического файлового дескриптора, IO :: Handle, IO :: File и т. Д. Пока что единственное, что кажется согласованным среди различныхароматизаторы в том, что все они имеют reftype из "GLOB".

Ответы [ 6 ]

24 голосов
/ 28 сентября 2010

Используйте функцию openhandle из Scalar :: Util :

openhandle FH

Возвращает FH, если FH может использоваться как файловый дескриптор и открыт, или FH является привязанная ручка В противном случае undef вернулся.

  $fh = openhandle(*STDIN);           # \*STDIN
  $fh = openhandle(\*STDIN);          # \*STDIN
  $fh = openhandle(*NOTOPEN);         # undef
  $fh = openhandle("scalar");         # undef

Текущая реализация похожа на ответ Грега Бэкона , но у нее есть некоторые дополнительные тесты.

13 голосов
/ 09 июля 2010

Помните, что вы можете сделать это:

$ perl -le '$fh = "STDOUT"; print $fh "Hi there"'
Hi there

Это обычная строка, но все же полезная в качестве дескриптора файла.

Просмотр источника IO::Handleего opened является тонкой оболочкой вокруг fileno, которая имеет удобное свойство:

Возвращает дескриптор файла для дескриптора файла или неопределенный, если дескриптор файла не являетсяopen.

Но есть одно предупреждение:

Файловые дескрипторы, связанные с объектами памяти с помощью новых функций open, могут возвращать неопределенное значение, даже если они открыты.

Похоже, что тест по направлениям

$@ = "";
my $fd = eval { fileno $maybefh };
my $valid = !$@ && defined $fd;

будет делать то, что вы хотите.

Приведенный ниже код проверяет представителей

  • объекты в памяти
  • именованные дескрипторы файлов
  • globs
  • ссылки на глобусы
  • имена глобусов
  • стандартный ввод
  • FileHandle экземпляров
  • IO::File экземпляров
  • труб
  • FIFO
  • розеток

Запустите его самостоятельно:

#! /usr/bin/perl

use warnings;
use strict;

use Fatal qw/ open /;
use FileHandle;
use IO::File;
use IO::Socket::INET;

my $SLEEP = 5;
my $FIFO  = "/tmp/myfifo";

unlink $FIFO;
my $pid = fork;
die "$0: fork" unless defined $pid;
if ($pid == 0) {
  system("mknod", $FIFO, "p") == 0 or die "$0: mknod failed";
  open my $fh, ">", $FIFO;
  sleep $SLEEP;
  exit 0;
}
else {
  sleep 1 while !-e $FIFO;
}

my @ignored = (\*FH1,\*FH2);
my @handles = (
  [0, "1",           1],
  [0, "hashref",     {}],
  [0, "arrayref",    []],
  [0, "globref",     \*INC],
  [1, "in-memory",   do {{ my $buf; open my $fh, "<", \$buf; $fh }}],
  [1, "FH1 glob",    do {{ open FH1, "<", "/dev/null"; *FH1 }}],
  [1, "FH2 globref", do {{ open FH2, "<", "/dev/null"; \*FH2 }}],
  [1, "FH3 string",  do {{ open FH3, "<", "/dev/null"; "FH3" }}],
  [1, "STDIN glob",  \*STDIN],
  [1, "plain read",  do {{ open my $fh, "<", "/dev/null"; $fh }}],
  [1, "plain write", do {{ open my $fh, ">", "/dev/null"; $fh }}],
  [1, "FH read",     FileHandle->new("< /dev/null")],
  [1, "FH write",    FileHandle->new("> /dev/null")],
  [1, "I::F read",   IO::File->new("< /dev/null")],
  [1, "I::F write",  IO::File->new("> /dev/null")],
  [1, "pipe read",   do {{ open my $fh, "sleep $SLEEP |"; $fh }}],
  [1, "pipe write",  do {{ open my $fh, "| sleep $SLEEP"; $fh }}],
  [1, "FIFO read",   do {{ open my $fh, "<", $FIFO; $fh }}],
  [1, "socket",      IO::Socket::INET->new(PeerAddr => "localhost:80")],
);

sub valid {
  local $@;
  my $fd = eval { fileno $_[0] };
  !$@ && defined $fd;
}

for (@handles) {
  my($expect,$desc,$fh) = @$_;
  print "$desc: ";

  my $valid = valid $fh;
  if (!$expect) {
    print $valid ? "FAIL\n" : "PASS\n";
    next;
  }

  if ($valid) {
    close $fh;
    $valid = valid $fh;
    print $valid ? "FAIL\n" : "PASS\n";
  }
  else {
    print "FAIL\n";
  }
}

print "Waiting for sleeps to finish...\n";

Все проходы на коробке Ubuntu 9.10, поэтому предостережение относительно объектов в памяти, по крайней мере, не вызывает беспокойства на этой платформе, по крайней мере.

1: PASS
hashref: PASS
arrayref: PASS
globref: PASS
in-memory: PASS
FH1 glob: PASS
FH2 globref: PASS
FH3 string: PASS
STDIN glob: PASS
plain read: PASS
plain write: PASS
FH read: PASS
FH write: PASS
I::F read: PASS
I::F write: PASS
pipe read: PASS
pipe write: PASS
FIFO read: PASS
socket: PASS
4 голосов
/ 15 ноября 2010

Но любой скаляр содержит что-то, что можно использовать в качестве дескриптора файла.Строки могут быть файловыми дескрипторами: тогда они являются дескрипторами пакетов.

Мы всегда использовали для этого Symbol::qualify().Я не знаю, является ли это все еще «способом», который обычно защищают, но он будет работать, если вам передадут дескрипторы голых слов (которые являются просто строками).Он проверяет пакет caller, квалифицируя его соответствующим образом.вот также Symbol::qualify_to_ref(), что, возможно, ближе к тому, что вы ищете.

Вот как они оба работают.В выводе ниже:

  1. Первый элемент в списке => - это то, что получается с помощью qualify
  2. Второй элемент в списке => - это то, что получается с помощью qualify_to_ref
  3. Третий элемент в списке => - это файл fileno возвращает второй элемент

Сценарий, который его производит, приведен ниже:

off to NotMain
 string    "stderr"       => main::stderr, GLOB(0x811720), fileno 2
 string    *stderr        => *NotMain::stderr, GLOB(0x879ec0), fileno undef
 string    *sneeze        => *NotMain::sneeze, GLOB(0x811e90), fileno undef
 string    *STDERR        => *main::STDERR, GLOB(0x835260), fileno 2
back to main
 string    *stderr        => *main::stderr, GLOB(0x879ec0), fileno 2
 string    "STDOUT"       => main::STDOUT, GLOB(0x8116c0), fileno 1
 string    *STDOUT        => *main::STDOUT, GLOB(0x811e90), fileno 1
 string    *STDOUT{IO}    => IO::File=IO(0x8116d0), GLOB(0x811e90), fileno 1
 string   \*STDOUT        => GLOB(0x8116c0), GLOB(0x8116c0), fileno 1
 string   "sneezy"        => main::sneezy, GLOB(0x879ec0), fileno undef
 string   "hard to type"  => main::hard to type, GLOB(0x8039e0), fileno 3
 string   $new_fh         => IO::Handle=GLOB(0x8046c0), IO::Handle=GLOB(0x8046c0), fileno undef
 string   "GLOBAL"        => main::GLOBAL, GLOB(0x891ff0), fileno 3
 string   *GLOBAL         => *main::GLOBAL, GLOB(0x835260), fileno 3
 string   $GLOBAL         => main::/dev/null, GLOB(0x817320), fileno 3
 string   $null           => GLOB(0x8907d0), GLOB(0x8907d0), fileno 4

off to NotMain
   glob    "stderr"       => main::stderr, GLOB(0x811720), fileno 2
   glob     stderr        => main::stderr, GLOB(0x811720), fileno 2
   glob     sneeze        => main::sneeze, GLOB(0x81e490), fileno undef
   glob    *sneeze        => GLOB(0x892b90), GLOB(0x892b90), fileno undef
   glob    *stderr        => GLOB(0x892710), GLOB(0x892710), fileno undef
   glob    *STDERR        => GLOB(0x811700), GLOB(0x811700), fileno 2
back to main
   glob    *stderr        => GLOB(0x811720), GLOB(0x811720), fileno 2
   glob     STDOUT        => main::STDOUT, GLOB(0x8116c0), fileno 1
   glob    "STDOUT"       => main::STDOUT, GLOB(0x8116c0), fileno 1
   glob    *STDOUT        => GLOB(0x8116c0), GLOB(0x8116c0), fileno 1
   glob    *STDOUT{IO}    => IO::File=IO(0x8116d0), GLOB(0x811d50), fileno 1
   glob   \*STDOUT        => GLOB(0x8116c0), GLOB(0x8116c0), fileno 1
   glob    sneezy         => main::sneezy, GLOB(0x879ec0), fileno undef
   glob   "sneezy"        => main::sneezy, GLOB(0x879ec0), fileno undef
   glob   "hard to type"  => main::hard to type, GLOB(0x8039e0), fileno 3
   glob   $new_fh         => IO::Handle=GLOB(0x8046c0), IO::Handle=GLOB(0x8046c0), fileno undef
   glob    GLOBAL         => main::GLOBAL, GLOB(0x891ff0), fileno 3
   glob   $GLOBAL         => main::/dev/null, GLOB(0x817320), fileno 3
   glob   *GLOBAL         => GLOB(0x891ff0), GLOB(0x891ff0), fileno 3
   glob   $null           => GLOB(0x8907d0), GLOB(0x8907d0), fileno 4

А вот сценарий, который генерирует этот вывод:

eval 'exec perl $0 ${1+"$@"}'
               if 0;

use 5.010_000;
use strict;
use autodie;
use warnings qw[ FATAL all ];

use Symbol;
use IO::Handle;

#define exec(arg)
BEGIN { exec("cpp $0 | $^X") }  # nyah nyah nyah-NYAH nhah!!
#undef  exec

#define CPP(FN, ARG) printf(" %6s %s => %s\n", main::short("FN"), q(ARG), FN(ARG))
#define QS(ARG)      CPP(main::qual_string, ARG)
#define QG(ARG)      CPP(main::qual_glob, ARG)
#define NL           say ""

sub comma(@);
sub short($);
sub qual($);
sub qual_glob(*);
sub qual_string($);

$| = 1;

main();
exit();

sub main {

    our $GLOBAL = "/dev/null";
    open GLOBAL;

    my $new_fh = new IO::Handle;

    open(my $null, "/dev/null");

    for my $str ($GLOBAL, "hard to type") {
        no strict "refs";
        *$str = *GLOBAL{IO};
    }

    fake_qs();

    QS(  *stderr       );
    QS(  "STDOUT"      );
    QS(  *STDOUT       );
    QS(  *STDOUT{IO}   );
    QS( \*STDOUT       );
    QS( "sneezy"       );
    QS( "hard to type" );
    QS( $new_fh        );
    QS( "GLOBAL"       );
    QS( *GLOBAL        );
    QS( $GLOBAL        );
    QS( $null          );

    NL;

    fake_qg();

    QG(  *stderr       );
    QG(   STDOUT       );
    QG(  "STDOUT"      );
    QG(  *STDOUT       );
    QG(  *STDOUT{IO}   );
    QG( \*STDOUT       );
    QG(  sneezy        );
    QG( "sneezy"       );
    QG( "hard to type" );
    QG( $new_fh        );
    QG(  GLOBAL        );
    QG( $GLOBAL        );
    QG( *GLOBAL        );
    QG( $null          );

    NL;

}

package main;

sub comma(@) { join(", " => @_) }

sub qual_string($) {
    my $string = shift();
    return qual($string);
}

sub qual_glob(*) {
    my $handle = shift();
    return qual($handle);
}

sub qual($) {
    my $thingie = shift();

    my $qname = qualify($thingie);
    my $qref  = qualify_to_ref($thingie);
    my $fnum  = do { no autodie; fileno($qref) };
    $fnum = "undef" unless defined $fnum;

    return comma($qname, $qref, "fileno $fnum");
}

sub short($) {
    my $name = shift();
    $name =~ s/.*_//;
    return $name;
}


sub fake_qg { &NotMain::fake_qg }
sub fake_qs { &NotMain::fake_qs }

package NotMain;  # this is just wicked

sub fake_qg {
    say "off to NotMain";
    QG(  "stderr"      );
    QG(   stderr       );
    QG(   sneeze       );
    QG(  *sneeze       );
    QG(  *stderr       );
    QG(  *STDERR       );
    say "back to main";
}

sub fake_qs {
    say "off to NotMain";
    package NotMain;
    QS(  "stderr"      );
    QS(  *stderr       );
    QS(  *sneeze       );
    QS(  *STDERR       );
    say "back to main";
}

Что я могу сказать?Иногда я действительно скучаю по препроцессору C.

Я просто знаю об этом мне поговорим.10

3 голосов
/ 28 сентября 2010

io_from_any из IO::Handle::Util заботится о повышении чего-либо до чего-то вменяемого.

2 голосов
/ 09 июля 2010

Вот выдержка из File :: Copy , определяющая, является ли переменная дескриптором файла:

my $from_a_handle = (ref($from)
  ? (ref($from) eq 'GLOB'
      || UNIVERSAL::isa($from, 'GLOB')
      || UNIVERSAL::isa($from, 'IO::Handle'))
  : (ref(\$from) eq 'GLOB'));
0 голосов
/ 15 марта 2011

Я склонен использовать:

 eval { $fh->can('readline') }

Или могу ('напечатать') в случае ручек, к которым я собираюсь написать.В основном это потому, что я действительно хочу иметь дело с файловыми дескрипторами только в любом случае, так что это точно определяет, может ли цель сделать то, что я от нее ожидаю.Если вы уже проверили, определен ли $ fh, вы, вероятно, можете оставить eval.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...