Динамическая загрузка модулей в Perl

Я хочу хранить множество функций в папке и включать их в основной скрипт.

Например, у меня есть эта библиотека ABC1.pm:

package ABC1;

use strict;
use warnings;

my $var1 = 'abc1';

sub func1 {
    print "$var1\n";
}

return 1;

Другой называется ABC2.pm:

package ABC2;

use strict;
use warnings;

my $var2 = 'abc2';

sub func2 {
    print "$var2\n";
}

return 1;

Оба файла сохраняются в папке библиотеки. Теперь я запускаю основной скрипт, который просматривает папку для всех файлов, которые находятся внутри, и пытается загрузить их:

#!/usr/bin/env perl
#script.pl

use strict;
use warnings;
BEGIN {push @INC, './libraries/.'}
# use ABC1;
# use ABC2;

my $dir = './libraries';
my @libraries;
opendir(DIR, $dir) or die $!;
while (my $file = readdir(DIR)) {
    next if ($file =~ m/^\./);
    print "$file\n";
    $file =~ s/\.pm$//;
    print "$file\n";
    push @libraries, $file;
}

eval {
    foreach my $library (@libraries){
        require $library;
        $library->import();
    }
    1;
} or do {
   my $error = $@;
   print $error;
};

ABC1->func1();
ABC2->func2();

Если я просто запустил его со статусом использования, введенным вручную, он заработает. Но если я хочу использовать фрагмент кода, который не прокомментирован, я получаю следующую ошибку:

Не удается найти ABC1 в @INC (@INC содержит: C: / Strawberry / perl / site / lib / MSWin32-x64-multi-thread C: / Strawberry / perl / site / lib C: / Strawberry / perl / vendor / lib C: / Strawberry / perl / lib. ./libraries/. ./libraries/.) в строке 25 abc.pl. Невозможно найти метод объекта "func1" через пакет "ABC1" (возможно, вы забыли загрузить "ABC1" "?) в строке 34 abc.pl.

Но библиотеки путей отображаются в @INC, поэтому я не понимаю, что происходит.


person nck    schedule 04.02.2020    source источник
comment
Проблема может заключаться в отсутствии расширения и отсутствии прав на чтение файла. Я не могу сейчас думать ни о чем другом.   -  person ikegami    schedule 04.02.2020
comment
Импорт во время выполнения ($library->import();) редко имеет смысл. Не та проблема, о которой вы спрашиваете.   -  person ikegami    schedule 04.02.2020
comment
next if ($file =~ m/^\./); должно быть next if $file =~ /^\./ || $file !~ /\.pm\z/;, потому что require ищет только .pm файлы.   -  person ikegami    schedule 04.02.2020
comment
Вы должны использовать $RealBin вместо . (полученного с помощью use FindBin qw( $RealBin );) для поиска файлов относительно вашей программы. Не та проблема, о которой вы спрашиваете.   -  person ikegami    schedule 04.02.2020
comment
BEGIN {push @INC, './libraries/.'} можно записать более чисто как use lib 'libraries'; (или use lib "$FindBin/libraries";, как предложено выше).   -  person ikegami    schedule 04.02.2020
comment
@ikegami на самом деле это была проблема, поэтому sytaxis Requiere включает расширение, а расширение use - нет.   -  person nck    schedule 04.02.2020
comment
Ой, да, получилось наоборот. require EXPR требуется путь   -  person ikegami    schedule 04.02.2020
comment
(В исходном комментарии я имел в виду, что в имени файла могло отсутствовать расширение.)   -  person ikegami    schedule 04.02.2020
comment
Твоя логика ускользает от меня. Если вы хотите загрузить все модули со всеми функциями, почему бы просто не добавить кучу use [Module] в верхней части кода? Если вы загружаете все функции, возможно, имеет смысл поместить их все в один модуль. Было бы разумнее хранить имена желаемых модулей в массиве и загружать только их динамически.   -  person Polar Bear    schedule 05.02.2020
comment
Возможно, вы неправильно сформулировали свой вопрос. Я предполагаю, что вы хотите знать, где хранить ваши сгенерированные модули в дереве Strawberry perl. Из вашего сообщения я вижу, что правильное расположение будет C: / Strawberry / perl / site / lib /.   -  person Polar Bear    schedule 05.02.2020


Ответы (1)


В то время как use MODULE и require MODULE ожидают имя модуля, require EXPR синтаксис require ожидает путь к файлу.

Это может быть путь относительно пути в @INC.

require "Foo/Bar.pm";

Это может быть путь относительно текущего каталога.

require "./Foo/Bar.pm";

Это может быть абсолютный путь.

require "/some/dir/Foo/Bar.pm";

Исправлено и очищено:

#!/usr/bin/env perl

use strict;
use warnings;

use FindBin qw( $RealBin );  # Get the location of this program.

my $plugins_dir_qfn = "$RealBin/libraries";

my @libraries;
opendir(my $dh, $plugins_dir_qfn) or die $!;
while (my $fn = readdir($dh)) {
    next if $fn =~ /^\./ || $fn !~ /\.pm\z/;

    my $qfn = "$plugins_dir_qfn/$fn";
    #print "$qfn\n";

    eval { require $qfn; }
        or warn("Can't load \"$qfn\": $@");
}

ABC1->func1();
ABC2->func2();
person ikegami    schedule 04.02.2020