2017-08-24 2 views
4

Ich schreibe ein Tool, das eine Reihe von anderen Perl-Konfigurationsdateien importieren muss. Die Dateien sind nicht mit Paketen verpackt und können ähnliche oder widersprüchliche Variablen/Funktionen haben. Ich habe nicht die Möglichkeit, das Format dieser Dateien zu ändern, also muss ich umgehen, was sie sind. Was ich denken wollte, war jeden in einen eindeutigen Namensraum zu importieren, aber ich habe keinen Weg gefunden, das zu tun, indem ich do, require oder use benutze. Wenn ich keine dynamischen Namen, nur einen fest codierten Namen verwende, kann ich es tun.include/eval Perl-Datei in eindeutigen Namespace zur Laufzeit definiert

so etwas wie dieses Wollen:

sub sourceTheFile { 
    my ($namespace, $file) = @_; 
    package $namespace; 
    do $file; 
    1; 
    return; 
} 

Das funktioniert nicht, weil das Paket Befehl für den Namen eine Konstante erfordert. Also versuche ich, etwas wie folgt aus:

sub sourceTheFile { 
    my ($namespace, $file) = @_; 
    eval "package $namespace;do $file;1;" 
    return; 
} 

Aber der Inhalt der Datei durch do gelesen werden, in dem main:: Umfang nicht das, was ich will platziert. Der Zielbereich wird erstellt, nur nicht von do aufgefüllt. (Ich versuchte erfordern, und nur eine gerade cat $file innerhalb der Eval als auch.)

Ich benutze Devel::Symdump, um zu überprüfen, dass die Namespaces korrekt erstellt werden oder nicht.

Beispiel Eingabedatei:

my $xyz = "some var"; 
%all_have_this = (common=>"stuff"); 

zusätzliche Herausforderung

die Antwort verwenden, die die temporäre Datei Build tut und nennst, kann ich diese Arbeit dynamisch machen, wie ich brauche. ABER, groß aber, wie referenziere ich jetzt die Daten innerhalb dieses neuen Namespace? Perl scheint nicht die Fähigkeit zu verlieren, einen Variablennamen aus einer Zeichenfolge zu erstellen und diese als Variable zu verwenden.

+2

Bitte poste ein [mcve]. – melpomene

+0

Es ist ein bisschen mehr Arbeit von vorne, aber ein Haken in '@ INC' wäre eine robustere Möglichkeit, dies zu tun. –

+0

Du erkennst, wenn du 'my $ xyz = '...' hast und du das in ein Paket legst, kannst du nicht von außerhalb des Pakets darauf zugreifen, richtig? –

Antwort

3

Ich bin mir nicht sicher, warum die eval nicht funktioniert hat. Vielleicht ein Fehler? Hier ist eine Problemumgehung mit einer temporären Datei. Dies funktioniert für mich:

use strict; 
use warnings; 

use Devel::Symdump; 
use File::Temp; 

my $file = './test.pl'; 
my $namespace = 'TEST'; 
{ 
    my $fh = File::Temp->new(); 
    print $fh "package $namespace;\n"; 
    print $fh "do '$file';\n"; 
    print $fh "1;\n"; 
    close $fh; 
    do $fh->filename; 
} 
+0

Bis jetzt scheint dies die dynamischste Option (kann im laufenden Betrieb durchgeführt werden, wenn mehr Daten eingelesen werden). – Bruce

3

Perl use und require Einrichtungen Verwendung von Haken machen Sie in @INC installiert haben könnte. Sie können einfach einen Haken installieren, die an einer bestimmten Stelle sieht man Module mit einem Präfix laden wählen:

package MyIncHook; 

use strict; 
use warnings; 

use autouse Carp => qw(croak); 

use File::Spec::Functions qw(catfile); 

sub import { 
    my ($class, $prefix, $location) = @_; 
    unshift @INC, _loader_for($prefix, $location); 
    return; 
} 

sub _loader_for { 
    my $prefix = shift; 
    my $location = shift; 

    $prefix =~ s{::}{/}g; 

    return sub { 
     my $self = shift; 
     my $wanted = shift; 

     return unless $wanted =~ /^\Q$prefix/; 

     my $path = catfile($location, $wanted); 
     my ($is_done); 

     open my $fh, '<', $path 
      or croak "Failed to open '$path' for reading: $!"; 

     my $loader = sub { 
      if ($is_done) { 
       close $fh 
        or croak "Failed to close '$path': $!"; 
       return 0; 
      } 
      if (defined (my $line = <$fh>)) { 
       $_ = $line; 
       return 1; 
      } 
      else { 
       $_ = "1\n"; 
       $is_done = 1; 
       return 1; 
      } 
     }; 

     (my $package = $wanted) =~ s{/}{::}g; 
     $package =~ s/[.]pm\z//; 

     my @ret = (\"package $package;", $loader); 
     return @ret; 
    } 
} 

__PACKAGE__; 
__END__ 

Offensichtlich ändern den Bau von $path nach Ihren Wünschen.

Sie können es wie folgt verwenden:

#!/usr/bin/env perl 

use strict; 
use warnings; 

use MyIncHook ('My::Namespace', "$ENV{TEMP}/1"); 

use My::Namespace::Rand; 

print $My::Namespace::Rand::settings{WARNING_LEVEL}, "\n"; 

wo $ENV{TEMP}/1/My/Namespace/Rand.pm enthält:

%settings = (
    WARNING_LEVEL => 'critical', 
); 

Ausgang:

C:\Temp> perl t.pl 
critical 

können Sie, natürlich, definieren Sie Ihre eigene Mapping von aus Modulnamen zu Dateinamen.

+0

Interessante Antwort. Ich bin mir nicht sicher, ob ich den Satz verstanden habe. "... weil es das' do' ist, das nach dem wahren Wert am Ende von '$ file'" * sucht. Warum sucht "do" nach einem wahren Wert, ist es nicht nötig, dass man nach einem wahren Wert sucht? Laut Dokumentation: 'do EXPR 'führt nur den Inhalt des Dateinamens' EXPR' als Perl-Skript aus. –

+0

@ HåkonHægland Danke für die Beobachtung. Ich habe diesen Teil aus meiner Antwort entfernt. Es ist möglich, dass es ein anderes Problem mit dem Code gibt, den der OP uns nicht zeigt. –

+0

Wie würdest du das dynamischer gestalten? Ich beginne mit einer Liste von zu lesenden Dateien (Befehlszeile). Wenn ich diese Liste analysiere, können diese Dateien auf andere verweisen (als Dateipfade in Strings), die ich auch dynamisch einlesen muss. – Bruce

Verwandte Themen