我想确定一个给定的标量是否拥有一个文件句柄。它可能已经从一个纯粹的文件句柄(即\*FH
),一个词法文件句柄,一个IO :: Handle,一个IO :: File等等传递给我。到目前为止,似乎在各种风格中一致的唯一东西是他们都有reftype
的"GLOB"
。确定标量是否拥有文件句柄的最佳方法是什么?
回答
使用来自Scalar::Util的openhandle功能:
openhandle FH
返回FH如果FH可以用作 文件句柄并且是打开的,或者FH是 并列句柄。否则,undef返回 。
$fh = openhandle(*STDIN); # \*STDIN $fh = openhandle(\*STDIN); # \*STDIN $fh = openhandle(*NOTOPEN); # undef $fh = openhandle("scalar"); # undef
当前实现类似于Greg Bacon's answer,但它有一些额外的测试。
请记住,你可以这样做:
$ perl -le '$fh = "STDOUT"; print $fh "Hi there"' Hi there
这是一个普通的字符串,但作为一个文件句柄还是有用的。
望着source of IO::Handle
,其opened
是一个简单包装的fileno
,它有一个方便的特性:
返回一个文件句柄的文件描述符,或未定义如果该文件句柄不开。
但有一点需要注意:
文件句柄通过开放新功能连接到内存中的对象可能会返回即使他们是开放的定义。
看来然后沿的
[email protected] = "";
my $fd = eval { fileno $maybefh };
my $valid = [email protected] && defined $fd;
线测试会做你想要什么。
下面的
-
内存
- 检查代表该代码对象
- 命名的文件句柄
- 水珠
- 水珠引用
- 水珠名称
- 标准输入
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 [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
它看起来像'tell'不是可移植的,并且未能检测到有效的东西:“标准流(如STDIN)的tell()的返回值取决于操作系统:它可能返回-1或其他值tell()在管道上,fifos和套接字通常返回-1。“ – 2010-07-10 10:17:48
@Chas尼斯赶上。查看更新的答案。 – 2010-07-10 13:33:41
真的很酷。让我想知道为什么CPAN上没有这样的东西。 – 2010-07-13 18:50:48
下面是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'));
Eww。 'UNIVERSAL :: isa'是一个糟糕的糟糕的做伊萨的方式。我更喜欢'eval {$ from-> isa('GLOB')}',这样继承和覆盖就能正常工作。所以说[isa文档。](http://search.cpan.org/~jesse/perl-5.12.1/lib/UNIVERSAL.pm)。我的意思是,他们给不做的例子是'$ is_io = UNIVERSAL :: isa($ fd,“IO :: Handle”); #BAD!' – 2010-07-09 21:55:25
@Robert P:实际上对于GLOB(和其他基本引用类型),最新的建议是使用Scalar :: Util :: reftype()。虽然我同意所有这些,但使用上面的代码会给你与File :: Copy一样的结果,这是一个核心模块,你必须经过一些努力才能使它不起作用。 – runrig 2010-07-11 21:53:03
@Robert P:我在PerlMonks(http://www.perlmonks.org/?node_id=615015)上对此进行了讨论,对于当前的最佳实践没有真正的结论性答案。 – runrig 2010-07-13 18:31:39
io_from_any
from IO::Handle::Util
负责将任何东西升级为理智的东西。
[IO :: Wrap']也有['wraponndle]](http://p3rl.org/IO::Wrap)... – 2010-11-15 07:54:16
但是任何标量包含可以用作文件句柄的东西。字符串可以是文件句柄:然后,它们是包句柄。
我们以前一直习惯使用Symbol::qualify()
。我不知道这是否仍然是“普遍倡导的”方式,但是如果您通过裸字句柄(它们只是字符串),它就会起作用。它会检查caller
的包装,并对其进行适当的限定。 这里也是Symbol::qualify_to_ref()
,这可能更接近你要找的东西。
以下是他们如何工作。在下面的输出:
- 在=的第一个项目>列表是什么获取由
qualify
- 在=>列表中的第二项进行是什么获取由
qualify_to_ref
- 在第三项由=>清单显示在第二项文件
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+"[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预处理器。
我只是知道这个人会让我谈论。 ☺
嘿,看,那个笨的气袋,也称为“perlcritic”,甚至不会一元'开放式'的福禄克。显示他们*知道的东西! – tchrist 2010-11-15 14:53:24
我倾向于使用:
eval { $fh->can('readline') }
,或者可以在手柄的情况下(“打印”),我打算就写。这主要是因为我真的只想用OO方式处理文件句柄,所以这能够准确地解决目标是否能做到我期望的。如果您已经检查了正在定义的$ fh,则可以忽略该eval。
- 1. 确定Object是否被更改的最佳方法是什么?
- 2. 确定变量是否为特定对象的最佳方法是什么?
- 3. 确定文件是否为空(php)的最佳方法?
- 4. 确定SQL Server中是否存在临时表的最佳方法是什么?
- 5. 确定多态连接表是否合适的最佳方法是什么
- 6. 什么是事件句柄?
- 7. 什么是事件句柄?
- 8. 确定SMTP邮件是否具有IMF标头的最佳方法?
- 9. 拥有可重用对话框的最佳方式是什么?
- 10. 在Java中确定文件的总字数的最佳方法是什么?
- 11. 确定HTTP文件上传的MIME类型的最佳方法是什么?
- 12. 访问非确定性资源的最佳方法是什么?
- 13. 什么是确定CSS问题源的最佳方法
- 14. 确定用户位置的最佳方法是什么?
- 15. 确定ISV目录路径的最佳方法是什么?
- 16. 确定函数是否有输出的最佳方法?
- 17. 确定哪些源文件不再需要的最佳方法是什么?
- 18. 确定域名在“hosts”文件中是否有效的最佳方法?
- 19. 查看RadioButtonList是否具有选定值的最佳方法是什么?
- 20. 加载正确的PhoneGap文件的最佳方式是什么
- 21. 检查用户是否可以上传文件的最佳方法是什么?
- 22. 检查网站文件夹是否存在的最佳方法是什么?
- 23. 确定WPF是否可以加载图像文件的最佳方法是什么?
- 24. 拥有可变数量模板参数的最佳方式是什么?
- 25. 确定字符串是否有值的最简洁的方法是什么?
- 26. 在监听器的句柄方法中调用控制器类的方法是否有最佳实践方法?
- 27. 翻译大量文本数据的最佳方法是什么?
- 28. 检测IDataReader是否为空的最佳方法是什么?
- 29. 检查视窗是否可见的最佳方法是什么?
- 30. 检查FileInputStream是否关闭的最佳方法是什么?
可能重复[如何判断Perl变量中的值的类型?](http://stackoverflow.com/questions/1731333/how-do-i-tell-what-type-of-value -is-in-a-perl-variable) – Ether 2010-07-09 16:55:20
哎呀抱歉,那不是确切的愚蠢。我希望我能收回那个近距离投票! (但链接仍然有点相关。) – Ether 2010-07-09 16:56:04
请参阅[何时'ref($变量)'返回'IO'?](http://stackoverflow.com/questions/2955428/when-does-refvariable-return-io )为一个类似的问题。 – Zaid 2010-07-10 10:27:00