我在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行上未打开的文件句柄母版上的print()。
错误是因为文件句柄LOGFILE
在sample.pm
模块下不可见。
如何实现这个概念?
我想在Main.pl
中打开一个文件,并且需要在不同的Perl模块中可以访问此文件句柄。
您看到此错误的原因是$main::LOGFILE
引用了包含文件名$LOGFILE
的标量变量sample
。文件句柄LOGFILE
是完全不同的变量。在这里,我们看到了两个具有相同名称的不同类型(标量vs文件句柄)变量的危险。
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;
您得到了非常详细的分析from Dave Cross。
[我想提供一种为所有要写入的模块提供干净的日志文件的方法。
介绍一个模块,该模块执行对子目录中日志文件的写入;由需要它的所有模块加载它。在该子目录中,使用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
中
开始Mod1:从主要嗨Mod2:另一个主要我为演示打印
START
,但不必从main
打开文件。
请根据需要进一步开发打印机模块。例如,添加一种可选的文件名传递方式,以便main
可以命名日志(通过改变参数的类型和数量),并添加一种可控制地关闭日志的方式。