Подтвердить что ты не робот

Каков наилучший способ определить, содержит ли скаляр дескриптор файла?

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

4b9b3361

Ответ 1

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

openhandle FH

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

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

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

Ответ 2

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

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

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

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

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

Но есть одна оговорка:

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

Тогда оказывается, что тест по линиям

[email protected] = "";
my $fd = eval { fileno $maybefh };
my $valid = [email protected] && defined $fd;

сделает то, что вы хотите.

В приведенном ниже коде проверяются представители

  • объекты в памяти
  • named filehandles
  • шарики
  • ссылки glob
  • имена glob
  • стандартный ввод
  • FileHandle экземпляры
  • IO::File экземпляры
  • трубы
  • FIFOs
  • розетки

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

#! /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 [email protected];
  my $fd = eval { fileno $_[0] };
  [email protected] && 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

Ответ 3

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

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

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

  • Первый элемент в списке = > - это то, что создается qualify
  • Второй элемент в списке = > - это то, что создается qualify_to_ref
  • Третий элемент в списке = > - это файл fileno возвращает второй элемент

script, который производит это, приведен ниже:

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

И heres script, которые генерируют этот вывод:

eval 'exec perl $0 ${1+"[email protected]"}'
               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.

Я просто знаю, что это заставит меня поговорить. ☺

Ответ 4

Здесь выдержка из 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'));

Ответ 6

Я обычно использую:

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

Или может ( "печатать" ) в случае ручек, на которые я намереваюсь писать. Это в основном потому, что я действительно хочу иметь дело с файловыми дескрипторами в OO-way, так что это точно решает, сможет ли цель сделать то, что я ожидаю от нее. Если вы уже проверили, что определен параметр $fh, вы, вероятно, можете оставить значение eval.