我即将开始一个脚本,可能有一些文件查找和操作,所以我想我应该看看一些软件包,可以帮助我;大多数情况下,我希望迭代(或搜索)的结果作为对象返回,这些对象将具有(基本)名称、路径、文件大小、uid、修改时间等某种属性。
问题是,我不经常这样做,而且容易忘记api;当发生这种情况时,我宁愿让代码在示例目录中运行,并转储对象中的所有属性,这样我就可以提醒自己哪里有可用的内容(显然,我想要"转储",以避免编写自定义打印输出)。但是,我知道以下内容:
列出对象的所有方法
"开箱即用的Perl不做对象自省。像Moose这样的类包装器将自省作为其实现的一部分,但是Perl的内置对象支持比这要原始得多。"
不管怎样,我看了看:
- " Perl中的文件和目录处理- Perl初学者网站" http://perl-begin.org/topics/files-and-directories/
…并开始研究那里提到的库(也有相关链接:rjbs的标题:Perl文件查找器的速度)。
所以,首先,File::Find::Object
似乎对我有用;这段代码:
use Data::Dumper;
@targetDirsToScan = ("./");
use File::Find::Object;
my $tree = File::Find::Object->new({}, @targetDirsToScan);
while (my $robh = $tree->next_obj()) {
#print $robh ."n"; # prints File::Find::Object::Result=HASH(0xa146a58)}
print Dumper($robh) ."n";
}
…打印:
# $VAR1 = bless( {
# 'stat_ret' => [
# 2054,
# 429937,
# 16877,
# 5,
# 1000,
# 1000,
# 0,
# '4096',
# 1405194147,
# 1405194139,
# 1405194139,
# 4096,
# 8
# ],
# 'base' => '.',
# 'is_link' => '',
# 'is_dir' => 1,
# 'path' => '.',
# 'dir_components' => [],
# 'is_file' => ''
# }, 'File::Find::Object::Result' );
# $VAR1 = bless( {
# 'base' => '.',
# 'is_link' => '',
# 'is_dir' => '',
# 'path' => './test.blg',
# 'is_file' => 1,
# 'stat_ret' => [
# 2054,
# 423870,
# 33188,
# 1,
# 1000,
# 1000,
# 0,
# '358',
# 1404972637,
# 1394828707,
# 1394828707,
# 4096,
# 8
# ],
# 'basename' => 'test.blg',
# 'dir_components' => []
…这主要是我想要的,除了stat
结果是一个数组,我必须知道它的布局(($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)
stat - perldoc.perl.org)来理解打印输出。
然后我研究了IO::All
,我喜欢它,因为它有utf-8处理(而且,说,套接字功能,这对我在同一脚本中执行不相关的任务很有用);我想用这个包裹代替问题是,我很难发现返回对象中的可用字段是什么;例如:
use Data::Dumper;
@targetDirsToScan = ("./");
use IO::All -utf8;
$io = io(@targetDirsToScan);
@contents = $io->all(0);
for my $contentry ( @contents ) {
#print Dumper($contentry) ."n";
# $VAR1 = bless( *Symbol::GEN298, 'IO::All::File' );
# $VAR1 = bless( *Symbol::GEN307, 'IO::All::Dir' ); ...
#print $contentry->uid . " -/- " . $contentry->mtime . "n";
# https://stackoverflow.com/q/24717210/printing-ret-of-ioall-w-datadumper
print Dumper %{*$contentry}; # doesn't list uid
}
…我得到这样的打印输出:
# $VAR1 = {
# '_utf8' => 1,
# 'constructor' => sub { "DUMMY" },
# 'is_open' => 0,
# 'io_handle' => undef,
# 'name' => './test.blg',
# '_encoding' => 'utf8',
# 'package' => 'IO::All'
# };
# $VAR1 = {
# '_utf8' => 1,
# 'constructor' => sub { "DUMMY" },
# 'mode' => undef,
# 'name' => './testdir',
# 'package' => 'IO::All',
# 'is_absolute' => 0,
# 'io_handle' => undef,
# 'is_open' => 0,
# '_assert' => 0,
# '_encoding' => 'utf8'
…它显然没有显示mtime
等属性——即使它们存在(如果你取消相应的打印行注释,你可以看到它们)。
我也尝试过Data::Printer
的(我如何在Perl中执行自省?)p()
函数-它打印与Dumper
完全相同的字段。我还尝试使用print Dumper %{ref ($contentry) . "::"};
(列出对象的所有方法- perlmonks.org),它打印的内容如下:
'O_SEQUENTIAL' => *IO::All::File::O_SEQUENTIAL,
'mtime' => *IO::All::File::mtime,
'DESTROY' => *IO::All::File::DESTROY,
...
'deep' => *IO::All::Dir::deep,
'uid' => *IO::All::Dir::uid,
'name' => *IO::All::Dir::name,
...
…但是如果您事先使用print $contentry->uid ...
行,则只能;否则,它们不会被列出!我想这和这个有关:
introspection -如何在Perl中列出给定对象或包的可用方法?# 911294
一般来说,您不能使用像Perl这样的动态语言这样做。包可能会定义一些您可以找到的方法,但它也可以动态地创建一些方法,这些方法在使用之前没有定义。此外,即使调用(有效的)方法也可能无法定义它。这就是动态语言的优点。:)
仍然打印字段的名称和类型—我想要字段的名称和值。
所以,我想我的主要问题是-我如何转储IO::All
结果,以便所有字段(包括stat
字段)都与其名称和值一起打印出来(主要是File::Find::Object
的情况)?
(我注意到IO::All
的结果可以是IO::All::File的类型,但是它的文档遵从"See IO::All",它根本没有明确地讨论IO::All::File
。我想,如果我可以"cast"%{*$contentry}
到IO::All::File
,也许然后mtime
等字段将被打印-但是这样的"cast"是可能的吗?
如果这是有问题的,是否有其他的包,这将允许内省的目录迭代结果的打印输出-但与个别stat
属性命名字段?
Perl进行自省,对象将告诉您它是什么类型的对象。
if ( $object->isa("Foo::Bar") ) {
say "Object is of a class of Foo::Bar, or is a subclass of Foo::Bar.";
}
if ( ref $object eq "Foo::Bar" ) {
say "Object is of the class Foo::Bar.";
}
else {
say "Object isn't a Foo::Bar object, but may be a subclass of Foo::Bar";
}
你还可以查看一个对象是否可以做某事:
if ( $object->can("quack") ) {
say "Object looks like a duck!";
}
Perl不能直接做的是给你一个特定对象可以做的所有方法的列表。
你也许可以改变一些方法。Perl对象存储在符号表中的包名称空间中。类通过Perl子例程实现。可以遍历包名称空间,然后找到所有子例程。
然而,我可以看到几个问题。首先,私有方法(您不应该使用的方法)和非方法子例程也将包括在内。根本分不清哪个是哪个。此外,父方法也不会被列出。
许多语言都可以为它们的对象生成这样的方法列表(我相信Python和Ruby都可以),但它们通常只给你一个列表,而不解释这些方法的作用。例如,File::Find::Object::Result
(由File::Find::Object
的next_obj
方法返回)有一个base
方法。它是做什么的?也许它就像basename
,给我文件名。不,它就像dirname
,给了我目录的名字。
同样,一些语言可以为对象和描述提供这些方法的列表。然而,这些描述依赖于程序员来维护并确保它们是正确的。不能保证。
Perl没有自省功能,但是存储在CPAN中的所有Perl模块都必须通过POD嵌入的文档进行记录,并且可以从命令行打印:
$ perldoc File::Find::Object
这是您在CPAN页面,http://Perldoc.perl.org和ActiveState的Perl文档中看到的文档。
还不错。这不是真正的自省,但文档通常都很好。毕竟,如果文档很糟糕,我可能一开始就不会安装那个模块。我一直使用perldoc
。我几乎不能记住我孩子的名字,更不用说我几个月没有使用过的Perl类的使用方法了,但是我发现使用perldoc
非常有效。
你不应该做的是使用Data::Dumper
来转储对象,并试图找出它们包含什么和可能的方法。一些cleaver程序员正在使用Inside-Out Objects来阻止偷窥者。
所以,Perl不像某些语言那样列出特定类的方法,但是perldoc
非常接近您所需要的。我很久没有使用File::Find::Object
了,但是通过perldoc
,我可能可以毫不费力地编写这样一个程序。
正如我对您上一个问题的回答,依赖Perl中的对象内部不是一个好主意。而是直接调用方法。
如果IO::All没有提供给你所需信息的方法,你可以为它编写自己的方法,使用IO::All提供的文档方法来组装这些信息…
use IO::All;
# Define a new method for IO::All::Base to use, but
# define it in a lexical variable!
#
my $dump_info = sub {
use Data::Dumper ();
my $self = shift;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Sortkeys = 1;
return Data::Dumper::Dumper {
name => $self->name,
mtime => $self->mtime,
mode => $self->mode,
ctime => $self->ctime,
};
};
$io = io('/tmp');
for my $file ( $io->all(0) ) {
print $file->$dump_info();
}
好吧,这是一个练习(和提醒我);下面是一些代码,我试图为所有stat
字段定义一个类(File::Find::Object::StatObj
)的访问器字段。然后,我从替换Perl中的类("覆盖"/"扩展")中获得IO::All::File
的hack。具有相同名称的类)?,其中增加了一个mtimef
字段,对应mtime
,只是作为提醒。
然后,只是为了看看两个库之间可以有什么样的接口,我让IO::All
进行迭代;将当前文件路径传递给File::Find::Object
,从中我们得到一个File::Find::Object::Result
——它被"黑"了,也显示了File::Find::Object::StatObj
;但只有在调用被黑客攻击的Result的full_components
(这也可能是一个单独的函数)之后才生成。注意,在本例中,您不会得到File::Find::Object::Result
的full_components
/dir_components
——因为显然不是File::Find::Object
在进行遍历,而是IO::All
。无论如何,结果是这样的:
# $VAR1 = {
# '_utf8' => 1,
# 'mtimef' => 1403956165,
# 'constructor' => sub { "DUMMY" },
# 'is_open' => 0,
# 'io_handle' => undef,
# 'name' => 'img/test.png',
# '_encoding' => 'utf8',
# 'package' => 'IO::All'
# };
# img/test.png
# > - $VAR1 = bless( {
# 'base' => 'img/test.png',
# 'is_link' => '',
# 'is_dir' => '',
# 'path' => 'img/test.png',
# 'is_file' => 1,
# 'stat_ret' => [
# 2054,
# 426287,
# 33188,
# 1,
# 1000,
# 1000,
# 0,
# '37242',
# 1405023944,
# 1403956165,
# 1403956165,
# 4096,
# 80
# ],
# 'basename' => undef,
# 'stat_obj' => bless( {
# 'blksize' => 4096,
# 'ctime' => 1403956165,
# 'rdev' => 0,
# 'blocks' => 80,
# 'uid' => 1000,
# 'dev' => 2054,
# 'mtime' => 1403956165,
# 'mode' => 33188,
# 'size' => '37242',
# 'nlink' => 1,
# 'atime' => 1405023944,
# 'ino' => 426287,
# 'gid' => 1000
# }, 'File::Find::Object::StatObj' ),
# 'dir_components' => []
# }, 'File::Find::Object::Result' );
我不确定这有多正确,但我喜欢的是我可以忘记字段在哪里;然后我可以重新运行转储程序,并看到我可以通过(*::Result) ->stat_obj->size
获得mtime
-这似乎有效(这里我只需要读取这些,而不是设置它们)。
无论如何,下面是代码:
use Data::Dumper;
my @targetDirsToScan = ("./");
use IO::All -utf8 ; # Turn on utf8 for all io
# try to "replace" the IO::All::File class
{ # https://stackoverflow.com/a/24726797/277826
package IO::All::File;
use IO::All::File; # -base; # just do not use `-base` here?!
# hacks work if directly in /usr/local/share/perl/5.10.1/IO/All/File.pm
# NB: field is a sub in /usr/local/share/perl/5.10.1/IO/All/Base.pm
field mtimef => undef; # hack
sub file {
my $self = shift;
bless $self, __PACKAGE__;
$self->name(shift) if @_;
$self->mtimef($self->mtime); # hack
#print("!! *haxx0rz'd* file() reporting inn");
return $self->_init;
}
1;
}
use File::Find::Object;
# based on /usr/local/share/perl/5.10.1/File/Find/Object/Result.pm;
# but inst. from /usr/local/share/perl/5.10.1/File/Find/Object.pm
{
package File::Find::Object::StatObj;
use integer;
use Tie::IxHash;
#use Data::Dumper;
sub ordered_hash { # https://stackoverflow.com/a/3001400/277826
#my (@ar) = @_; #print("# ". join(",",@ar) . "n");
tie my %hash => 'Tie::IxHash';
%hash = @_; #print Dumper(%hash);
%hash
}
my $fields = ordered_hash(
# from http://perldoc.perl.org/functions/stat.html
(map { $_ => $_ } (qw(
dev ino mode nlink uid gid rdev size
atime mtime ctime blksize blocks
)))
); #print Dumper(%{$fields});
use Class::XSAccessor
#accessors => %{$fields}, # cannot - is seemingly late
# ordered_hash gets accepted, but doesn't matter in final dump;
#accessors => { (map { $_ => $_ } (qw(
accessors => ordered_hash( (map { $_ => $_ } (qw(
dev ino mode nlink uid gid rdev size
atime mtime ctime blksize blocks
))) ),
#))) },
;
use Fcntl qw(:mode);
sub new
{
#my $self = shift;
my $class = shift;
my @stat_arr = @_; # the rest
my $ic = 0;
my $self = {};
bless $self, $class;
for my $k (keys %{$fields}) {
$fld = $fields->{$k};
#print "$ic '$k' '$fld' ".join(", ",$stat_arr[$ic])." ; ";
$self->$fld($stat_arr[$ic]);
$ic++;
}
#print "n";
return $self;
}
1;
}
# try to "replace" the File::Find::Object::Result
{
package File::Find::Object::Result;
use File::Find::Object::Result;
#use File::Find::Object::StatObj; # no, has no file!
use Class::XSAccessor replace => 1,
accessors => {
(map { $_ => $_ } (qw(
base
basename
is_dir
is_file
is_link
path
dir_components
stat_ret
stat_obj
)))
}
;
#use Fcntl qw(:mode);
#sub new # never gets called
sub full_components
{
my $self = shift; #print("NEWCOMPn");
my $sobj = File::Find::Object::StatObj->new(@{$self->stat_ret()});
$self->stat_obj($sobj); # add stat_obj and its fields
return
[
@{$self->dir_components()},
($self->is_dir() ? () : $self->basename()),
];
}
1;
}
# main script start
my $io = io($targetDirsToScan[0]);
my @contents = $io->all(0); # Get all contents of dir
for my $contentry ( @contents ) {
print Dumper %{*$contentry};
print $contentry->name . "n"; # img/test.png
# get a File::Find::Object::Result - must instantiate
# a File::Find::Object; just item_obj() will return undef
# right after instantiation, so must give it "next";
# no instantition occurs for $tro, though!
#my $tffor = File::Find::Object->new({}, ($contentry->name))->next_obj();
my $tffo = File::Find::Object->new({}, ("./".$contentry->name));
my $tffos = $tffo->next(); # just a string!
$tffo->_calc_current_item_obj(); # unfortunately, this will not calculate dir_components ...
my $tffor = $tffo->item_obj();
# ->full_components doesn't call new, either!
# must call full_compoments, to generate the fields
# (assign to unused variable triggers it fine)
# however, $arrref_fullcomp will be empty, because
# File::Find::Object seemingly calcs dir_components only
# if it is traversing a tree...
$arrref_fullcomp = $tffor->full_components;
#print("# ".$tffor->stat_obj->size."n"); # seems to work
print "> ". join(", ", @$arrref_fullcomp) ." - ". Dumper($tffor);
}