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

时间:2017-11-11 06:48:09

标签: perl

我在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行。

错误是因为LOGFILE模块下无法看到文件句柄sample.pm

如何实施这个概念?

我想在Main.pl中打开一个文件,我需要这个文件句柄才能在不同的Perl模块中访问。

2 个答案:

答案 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()。我删除了原型,说它没有参数(尽管你用&调用它的事实会关闭参数检查!)

其他几点。

  • 您不需要-wuse warnings。删除-w
  • 您不需要use strictuse strict 'refs'。删除后者。
  • 具有所有小写名称的模块保留用于称为 pragma 的特殊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;

答案 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可以命名日志(通过改变参数的类型和数量),并添加一种可控制地关闭日志的方法,