从模块访问主程序中定义的文件句柄

问题描述 投票:0回答:2

我在Perl中有以下有关文件处理程序访问的查询。

请考虑以下代码片段,它描述了确切的场景。

Main.pl

#!/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 );

sample.pm

#!/usr/bin/perl -w
package sample;

sub func() {
    print $main::LOGFILE ( "Printing in subroutine\n" );
}

执行上述代码段时,出现以下错误。

在/home/rxa3kor/Mastering_Perl/sample.pm第6行上未打开的文件句柄母版上的print()。

错误是因为文件句柄LOGFILEsample.pm模块下不可见。

如何实现这个概念?

我想在Main.pl中打开一个文件,并且需要在不同的Perl模块中可以访问此文件句柄。

perl module filehandle
2个回答
2
投票

您看到此错误的原因是$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()。我删除了原型,说它不带任何参数(尽管您用&调用它的事实关闭了参数检查!)

其他几点。

  • 您不需要-wuse warnings。删除-w
  • 您不需要use strictuse strict 'refs'。删除后者。
  • 具有所有小写字母名称的模块保留给称为pragmas的Perl特殊功能。不要这样命名您的模块。
  • $LOGFILE不必是程序包变量(用our定义)。只需使其成为词汇表(用my定义)即可。
  • [没有理由用&调用子例程(实际上,它有一些缺点会让您感到困惑)。
  • 除非您知道子程序的用途,否则不要用原型定义子程序。
  • 不需要模块中的shebang行。
  • 在模块中使用strictwarnings

我会这样写你的代码:

# 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;

2
投票

您得到了非常详细的分析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可以命名日志(通过改变参数的类型和数量),并添加一种可控制地关闭日志的方式。

© www.soinside.com 2019 - 2024. All rights reserved.