当我运行下面的代码时,我得到了
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;
}
答案 0 :(得分:7)
你做错了是使用字符串“F”作为文件句柄。这个
从来没有像这样的东西;你可以用一个裸字作为
文件句柄(open FH, ...; print FH ...
),或者你可以传入
空标量和perl会为其分配一个新的打开文件对象
变量。但是如果你传入字符串F,那么你需要参考
然后处理为F
,而不是$fh
。但是,不要这样做。
请改为:
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
那样对其进行别名:
sub open_yaml_with_lock {
open $_[0], '+<', $_[1] or die $!;
...
}
open_yaml_with_lock(my $fh, 'filename');
write_yaml_with_lock($fh);
但实际上,这是一团糟。把这些东西放在一个物体里。制作new
打开并锁定文件。添加write
方法。完成。现在你可以
重用这段代码(并让其他人做同样的事情)而不用担心
弄错了。减轻压力。
我们在这里做的另一件事是检查错误。是的,磁盘可以
失败。文件可能被拼写错误。如果你幸福地忽略了返回值
开放和群集,然后你的程序可能没有做你的想法
它正在做。该文件可能无法打开。该文件可能不是
锁定得当。有一天,你的程序无法正常工作
因为你拼写“文件”为“flie”并且文件无法打开。
几个小时你会抓挠头脑,想知道发生了什么。
最终,你会放弃,回家,稍后再试。这次,
你不会错误地输入文件名,它会起作用。几个小时会
被浪费了。你会比你应该早几年死去
因为累积的压力。所以只需use autodie
或在系统调用后编写or
die $!
,这样就会收到错误消息
出了点问题!
如果您在顶部写了use autodie qw/open flock
seek close/
,那么您的脚本将是正确的。 (实际上,你也应该检查一下
工作或使用“打印”
File::Slurp或
syswrite
,因为autodie无法检测到失败的print
语句。)
无论如何,总结一下:
定义open $fh
时不要$fh
。写open my $fh
给
避免考虑这个。
始终检查系统调用的返回值。让autodie做 这适合你。
不要保持全球状态。不要写一堆函数 意图是一起使用,但依赖于隐含的先决条件 像一个打开的文件。如果函数有前置条件,请将它们放入 一个类并使构造函数满足前提条件。 这样,您就不会意外地编写错误的代码!
<强>更新强>
好的,这是如何使这更多的OO。首先,我们将做“纯Perl”OO 然后使用Moose。麋是 我将用于任何实际工作; “纯粹的Perl”只是为了 为了让那些对OO和OO都不熟悉的人容易理解 的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
}
然后,我们可以在主程序中使用该类:
use LockedYAML;
my $resource = LockedYAML->new('filename');
print "Our object looks like: ". Dumper($resource->object);
$resource->write({ new => 'stuff' });
错误将抛出异常,可以使用 Try::Tiny和YAML 只要实例存在,文件就会保持锁定状态。你可以 当然,一次有很多LockedYAML对象,这就是我们的原因 做到了OO。
最后,穆斯版本:
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版本更长,但运行时一致性更高 检查并且更容易增强。因人而异。
答案 1 :(得分:2)
来自文档:
open FILEHANDLE,EXPR
如果FILEHANDLE是未定义的标量变量(或数组或散列) element)为变量分配对新匿名的引用 filehandle,否则如果FILEHANDLE是表达式,则其值为 用作真正的文件句柄的名称。 (这被认为是 符号引用,所以“use strict'refs'”应该 不生效。)
这里的Filehandle是一个表达式(“F”)所以它的值被用作你想要的真实文件句柄的名称。 (一个名为F的文件句柄)。然后......文档说“use strict'refs'”不应该有效,因为你使用F作为符号引用。
(第1行use strict;
包含strict 'refs'
。)
你刚才开始说:
my $fh;
这样做会有效,因为$ fh会成为对新匿名文件句柄的引用,而Perl不会尝试将它用作符号引用。
这有效:
#!/usr/bin/perl
my $global_fh;
open_filehandle(\$global_fh);
use_filehandle(\$global_fh);
sub open_filehandle {
my ($fh)=@_;
open($$fh, ">c:\\temp\\testfile") || die;
}
sub use_filehandle {
my($fh) = @_;
# Print is pecular that it expects the next token to be the filehandle
# or a simple scalar. Thus, print $$fh "Hello, world!" will not work.
my $lfh = $$fh;
print $lfh "Hello, world!";
close($$fh);
}
或者你可以做其他海报建议的内容并直接使用$ _ [1],但这有点难以阅读。
答案 2 :(得分:2)
如果直接在sub中使用该值,它将起作用:
use strict;
use warnings;
use autodie;
my $fh;
yada($fh);
print $fh "testing, testing";
sub yada {
open $_[0], '>', 'yada.gg';
}
或作为参考:
yada(\$fh);
sub yada {
my $handle = shift;
open $$handle, '>', 'yada.gg';
}
或者更好的是,返回一个文件句柄:
my $fh = yada($file);
sub yada {
my $inputfile = shift;
open my $gg, '>', $inputfile;
return $gg;
}
答案 3 :(得分:1)
替换
my $fh = "F"; # text and also a ref in nonstrict mode
与
my $fh = \*F; # a reference, period
当然,最好还是使用词法文件句柄,就像open my $fd, ... or die ...
一样,但这并不总是可行的,例如你有预定义的STDIN
。在这种情况下,请在\*FD
适合的任何地方使用$fd
。
还有一个旧脚本的情况,您必须注意全局FD的打开和关闭位置。