如何将文件句柄传递给函数? [英] How to pass a file handle to a function?

查看:46
本文介绍了如何将文件句柄传递给函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我运行下面的代码时,我得到

When I run the code below I get

Can't use string ("F") as a symbol ref while "strict refs" in use at ./T.pl line 21.

第 21 行在哪里

flock($fh, LOCK_EX);

我做错了什么?

#!/usr/bin/perl

use strict;
use warnings;
use Fcntl ':flock', 'SEEK_SET'; # file locking
use Data::Dumper;
# use xx;

my $file = "T.yaml";
my $fh = "F";
my $obj = open_yaml_with_lock($file, $fh);

$obj->{a} = 1;

write_yaml_with_lock($obj, $fh);

sub open_yaml_with_lock {
    my ($file, $fh) = @_;

    open $fh, '+<', $file;
    flock($fh, LOCK_EX);
    my $obj = YAML::Syck::LoadFile($fh);

    return $obj;
}

sub write_yaml_with_lock {
    my ($obj, $fh) = @_;

    my $yaml = YAML::Syck::Dump($obj);
    $YAML::Syck::ImplicitUnicode = 1;
    seek $fh,0, SEEK_SET;   # seek back to the beginning of file

    print $fh $yaml . "---\n";
    close $fh;
}

推荐答案

您做错的是使用字符串F"作为文件句柄.这从来没有奏效过;你可以使用一个裸词作为文件句柄(open FH, ...; print FH ...),或者你可以传入一个空标量和 perl 会为其分配一个新的打开文件对象多变的.但是如果传入字符串F,那么就需要参考然后处理为 F,而不是 $fh.但是,不要那样做.

What you're doing wrong is using the string "F" as a filehandle. This has never been something that's worked; you could use a bareword as a filehandle (open FH, ...; print FH ...), or you could pass in an empty scalar and perl would assign a new open file object to that variable. But if you pass in the string F, then you need to refer to then handle as F, not $fh. But, don't do that.

改为这样做:

sub open_yaml_with_lock {
    my ($file) = @_;

    open my $fh, '+<', $file or die $!;
    flock($fh, LOCK_EX) or die $!;

    my $obj = YAML::Syck::LoadFile($fh); # this dies on failure
    return ($obj, $fh);
}

我们在这里做了几件事.一,我们不存储全局文件句柄.全局状态使你的程序极其难以理解——你的 10 行帖子让我很难受——并且应该避免.只要返回文件句柄,如果你想保持它.或者,您可以像 open 那样为它设置别名:

We're doing several things here. One, we're not storing the filehandle in a global. Global state makes your program extremely difficult to understand -- I had a hard time with your 10 line post -- and should be avoided. Just return the filehandle, if you want to keep it around. Or, you can alias it like open does:

sub open_yaml_with_lock {
    open $_[0], '+<', $_[1] or die $!;
    ...
}

open_yaml_with_lock(my $fh, 'filename');
write_yaml_with_lock($fh);

但实际上,这是一团糟.把这些东西放在一个对象中.制作 new打开并锁定文件.添加一个 write 方法.完毕.现在你可以重用此代码(并让其他人也这样做)而不必担心出错了.减轻压力.

But really, this is a mess. Put this stuff in an object. Make new open and lock the file. Add a write method. Done. Now you can reuse this code (and let others do the same) without worrying about getting something wrong. Less stress.

我们在这里做的另一件事是检查错误.是的,磁盘可以失败.文件可能会被打错.如果你幸福地忽略了返回值open and flock,那么你的程序可能没有按照你的想法去做它在做.文件可能无法打开.该文件可能不是正确锁定.有一天,你的程序将无法正常运行因为您将file"拼写为flie"并且无法打开该文件.你会挠头好几个小时想知道发生了什么.最终,你会放弃,回家,稍后再试.这次,你不会打错文件名,它会起作用.几个小时将被浪费了.你会比你应该死的早几年因为累积的压力.所以只需 use autodie 或编写 或die $! 在您的系统调用之后,以便您在以下情况下收到错误消息出了点问题!

The other thing we're doing here is checking errors. Yup, disks can fail. Files can be typo'd. If you blissfully ignore the return value of open and flock, then your program may not be doing what you think it's doing. The file might not be opened. The file might not be locked properly. One day, your program is not going to work properly because you spelled "file" as "flie" and the file can't be opened. You will scratch your head for hours wondering what's going on. Eventually, you'll give up, go home, and try again later. This time, you won't typo the file name, and it will work. Several hours will have been wasted. You'll die several years earlier than you should because of the accumulated stress. So just use autodie or write or die $! after your system calls so that you get an error message when something goes wrong!

如果你写了use autodie qw/open flock,你的脚本就是正确的在顶部寻找 close/.(实际上,您还应该检查打印"工作或使用File::Slurpsyswrite,因为 autodie 无法检测到失败的 print 语句.)

Your script would be correct if you wrote use autodie qw/open flock seek close/ at the top. (Actually, you should also check that "print" worked or use File::Slurp or syswrite, since autodie can't detect a failing print statement.)

总之,总结一下:

  • 当定义了 $fh 时,不要 open $fh.写 open my $fh 到避免考虑这个.

  • Don't open $fh when $fh is defined. Write open my $fh to avoid thinking about this.

始终检查系统调用的返回值.让 autodie 做这是给你的.

Always check the return values of system calls. Make autodie do this for you.

不要保持全局状态.不要写一堆函数旨在一起使用,但依赖于隐含的先决条件就像一个打开的文件.如果函数有先决条件,把它们放在一个类并使构造函数满足先决条件.这样,您就不会不小心写出错误代码!

Don't keep global state. Don't write a bunch of functions that are meant to be used together but rely on implicit preconditions like an open file. If functions have preconditions, put them in a class and make the constructor satisfy the preconditions. This way, you can't accidentally write buggy code!

更新

好的,下面是如何使这更 OO.首先我们将做纯 Perl" OO然后使用 Moose.驼鹿是我将用于任何实际工作的东西;纯 Perl"只是为了为了让面向对象和面向对象的新手更容易理解Perl.

OK, here's how to make this more OO. First we'll do "pure Perl" OO and then use Moose. Moose is what I would use for any real work; the "pure Perl" is just for the sake of making it easy to understand for someone new to both OO and Perl.

package LockedYAML;
use strict;
use warnings;

use Fcntl ':flock', 'SEEK_SET';
use YAML::Syck;

use autodie qw/open flock sysseek syswrite/;

sub new {
    my ($class, $filename) = @_;
    open my $fh, '+<', $filename;
    flock $fh, LOCK_EX;

    my $self = { obj => YAML::Syck::LoadFile($fh), fh => $fh };
    bless $self, $class;
    return $self;
}

sub object { $_[0]->{obj} }

sub write {
    my ($self, $obj) = @_;
    my $yaml = YAML::Syck::Dump($obj);

    local $YAML::Syck::ImplicitUnicode = 1; # ensure that this is
                                            # set for us only

    my $fh = $self->{fh};

    # use system seek/write to ensure this really does what we
    # mean.  optional.
    sysseek $fh, 0, SEEK_SET;
    syswrite $fh, $yaml;

    $self->{obj} = $obj; # to keep things consistent
}

然后,我们可以在主程序中使用该类:

Then, we can use the class in our main program:

use LockedYAML;

my $resource = LockedYAML->new('filename');
print "Our object looks like: ". Dumper($resource->object);

$resource->write({ new => 'stuff' });

错误会抛出异常,可以用Try::Tiny 和 YAML只要实例存在,文件就会保持锁定状态.你可以的当然,同时有许多 LockedYAML 对象,这就是为什么我们成功了.

Errors will throw exceptions, which can be handled with Try::Tiny, and the YAML file will stay locked as long as the instance exists. You can, of course, have many LockedYAML objects around at once, that's why we made it OO.

最后,Moose 版本:

And finally, the Moose version:

package LockedYAML;
use Moose;

use autodie qw/flock sysseek syswrite/;

use MooseX::Types::Path::Class qw(File);

has 'file' => (
    is       => 'ro',
    isa      => File,
    handles  => ['open'],
    required => 1,
    coerce   => 1,
);

has 'fh' => (
    is         => 'ro',
    isa        => 'GlobRef',
    lazy_build => 1,
);

has 'obj' => (
    is         => 'rw',
    isa        => 'HashRef', # or ArrayRef or ArrayRef|HashRef, or whatever
    lazy_build => 1,
    trigger    => sub { shift->_update_obj(@_) },
);

sub _build_fh {
    my $self = shift;
    my $fh = $self->open('rw');
    flock $fh, LOCK_EX;
    return $fh;
}

sub _build_obj {
    my $self = shift;
    return YAML::Syck::LoadFile($self->fh);
}

sub _update_obj {
    my ($self, $new, $old) = @_;
    return unless $old; # only run if we are replacing something

    my $yaml = YAML::Syck::Dump($new);

    local $YAML::Syck::ImplicitUnicode = 1;

    my $fh = $self->fh;
    sysseek $fh, 0, SEEK_SET;
    syswrite $fh, $yaml;

    return;
}

用法类似:

 use LockedYAML;

 my $resource = LockedYAML->new( file => 'filename' );
 $resource->obj; # the object
 $resource->obj( { new => 'object' }); # automatically saved to disk

Moose 版本更长,但运行时一致性更高检查并且更容易增强.天啊.

The Moose version is longer, but does a lot more runtime consistency checking and is easier to enhance. YMMV.

这篇关于如何将文件句柄传递给函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆