我在Perl中有关于访问文件处理程序的以下查询。
请考虑以下代码片段,其中描述了确切的方案。
#!/usr/bin/perl -w
use warnings;
use strict;
use strict 'refs';
use File::Basename;
use Fcntl ':flock';
use feature qw/say switch/;
use File::Spec::Functions;
use File::Find;
require( "/home/rxa3kor/Mastering_Perl/sample.pm" );
our $LOGFILE = "sample";
open( LOGFILE, ">$LOGFILE" ) or die "__ERROR: can't open file\n'", $LOGFILE, "'!\n";
flock( LOGFILE, LOCK_EX );
print LOGFILE ( "Tool Start\n" );
&sample::func();
flock( LOGFILE, LOCK_UN );
close( LOGFILE );
#!/usr/bin/perl -w
package sample;
sub func() {
print $main::LOGFILE ( "Printing in subroutine\n" );
}
当我执行上述代码片段时,我收到以下错误。
打开()在未打开的文件句柄上掌握/home/rxa3kor/Mastering_Perl/sample.pm第6行。
错误是因为LOGFILE
模块下无法看到文件句柄sample.pm
。
如何实施这个概念?
我想在Main.pl
中打开一个文件,我需要这个文件句柄才能在不同的Perl模块中访问。
答案 0 :(得分:2)
您发现此错误的原因是$main::LOGFILE
引用了包含文件名$LOGFILE
的标量变量sample
。文件句柄LOGFILE
是一个完全不同的变量。在这里,我们看到了两个具有相同名称的不同类型变量(标量与文件句柄)的危险。
Bareword文件句柄(没有附加信号的大写字母,您使用的类型)是稍微奇怪的变量。他们不需要印记,所以你不应该使用印记。因此,最简单的解决方法是删除$
。
sub func()
{
print main::LOGFILE ("Printing in subroutine\n");
}
但是使用像这样的全局变量是一个糟糕的主意。它会很快导致你的代码变成一个难以维护的混乱。
使用 lexical 文件句柄并将其传递到子例程中会更好。
our $LOGFILE="sample";
open( my $log_fh, ">$LOGFILE" ) or die "__ERROR: can't open file\n'",$LOGFILE,"'!\n";
flock( $log_fh, LOCK_EX );
print $log_fh ("Tool Start\n");
&sample::func($log_fh);
flock( $log_fh, LOCK_UN );
close( $log_fh );
在sample.pm
:
sub func
{
my ($fh) = @_;
print $fh ("Printing in subroutine\n");
}
请注意,我现在正在将参数传递给func()
。我删除了原型,说它没有参数(尽管你用&
调用它的事实会关闭参数检查!)
其他几点。
-w
和use warnings
。删除-w
。use strict
和use strict 'refs'
。删除后者。$LOGFILE
不需要成为包变量(使用our
定义)。只需将其设为词汇(使用my
定义)。&
来调用子程序(事实上,它有一些让你感到困惑的缺点)。strict
和warnings
。我写这样的代码:
# main.pl
use warnings;
use strict;
use File::Basename; # Not used. Remove?
use Fcntl ':flock'; # Not used. Remove?
use feature qw/say switch/;
use File::Spec::Functions; # Not used. Remove?
use File::Find; # Not used. Remove?
use Sample;
my $LOGFILE = 'sample';
# Lexical filehandle. Three-arg version of open()
open( my $log_fh, '>', $LOGFILE )
or die "__ERROR: can't open file\n'$LOGFILE'!\n";
flock( $log_fh, LOCK_EX );
print $log_fh ("Tool Start\n");
Sample::func($log_fh);
flock( $log_fh, LOCK_UN );
close( $log_fh );
和...
package Sample;
use strict;
use warnings;
sub func {
my ($fh) = @_;
print $fh ("Printing in subroutine\n");
}
1;
答案 1 :(得分:2)
您已经获得了非常详细的分析from Dave Cross。
在这里,我想提供一种方法,为所有要写入的模块提供干净的日志文件。
引入一个模块,该模块执行对sub中日志文件的写入;由需要它的所有模块加载它。在该子程序中,使用state文件句柄打开要追加的日志文件,因此调用stays open。然后模块通过调用此子进行写入,这可以通过main
的调用启动。
记录器模块
package LogAll;
use warnings;
use strict;
use feature qw(say state);
use Carp qw(croak);
use Exporter qw(import);
our @EXPORT_OK = qw(write_log);
sub write_log {
state $fh = do { # initialize; stays open across calls
my $log = 'LOG_FILE.txt';
open my $afh, '>>', $log or croak "Can't open $log: $!";
$afh;
};
say $fh $_ for @_;
}
1;
另外两个需要记录的模块在这个例子中实际上是相同的;这是一个
package Mod1;
use warnings;
use strict;
use Exporter qw(import);
use LogAll qw(write_log);
our @EXPORT_OK = qw(f1);
sub f1 {
write_log(__PACKAGE__ . ": @_");
}
1;
主要
use warnings;
use strict;
use LogAll qw(write_log);
use Mod1 qw(f1);
use Mod2 qw(f2);
write_log('START');
f1("hi from " . __PACKAGE__);
f2("another " . __PACKAGE__);
运行会生成文件LOG_FILE.txt
START Mod1: hi from main Mod2: another main
我打印START
进行演示,但无需从main
打开该文件。
请根据需要进一步开发打印机模块。例如,以及可选地传递文件名的方法,以便main
可以命名日志(通过改变参数的类型和数量),并添加一种可控制地关闭日志的方法,