Почему я не могу загрузить библиотеку Perl при использовании функции `do`? - PullRequest
4 голосов
/ 10 января 2010

Я новичок в Perl и обновляю старый сайт Perl. Кажется, что каждый файл .pl имеет эту строку вверху:

do "func.inc";

Итак, я решил, что смогу использовать этот файл для пометки подпрограммы для глобального использования.

func.inc

#!/usr/bin/perl
sub foobar
{
    return "Hello world";
}

index.pl

#!/usr/bin/perl
do "func.inc";
print "Content-type: text/html\n\n";
print foobar();

Однако я получаю эту ошибку:

Undefined subroutine &main::foobar called at /path/to/index.pl line 4.

Оба файла находятся в одном каталоге, и в func.inc уже есть тоны сабвуферов, которые используются по всему сайту. Однако сценарий работает в производственной среде Linux, но не работает в моей среде разработки Windows 7 (я использую ActivePerl).

Обновление:

Похоже, файл не включен; подпрограмма работает, если файл включен с использованием абсолютного пути ...

do "C:/path/to/func.inc";

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

Как мне заставить do работать, используя относительный путь на моем компьютере с Windows 7?

Обновление 2:

Я использовал переключатель Perl -T. К сожалению, это удаляет "." из @INC, и поэтому мешает нам использовать относительные пути для do. Я удалил этот переключатель, и старый код теперь работает. Я знаю, что это не очень хорошая практика, но, к сожалению, я работаю со старым кодом, поэтому у меня нет выбора.

Ответы [ 5 ]

6 голосов
/ 10 января 2010

Документация perlfunc для do читает

  • до EXPR
    Использует значение EXPR в качестве имени файла и выполняет содержимое файла как скрипт Perl.

    do 'stat.pl';
    

    так же, как

    eval `cat stat.pl`;
    

    за исключением того, что он более эффективен и лаконичен, отслеживает текущее имя файла для сообщений об ошибках, ищет каталоги @INC и обновляет %INC, если файл найден.

Чтобы увидеть все это в действии, скажем, C:\Cygwin\tmp\mylib\func.inc выглядит как

sub hello {
  print "Hello, world!\n";
}

1;

и мы используем его в следующей программе:

#!/usr/bin/perl

use warnings;
use strict;

# your code may have unshift @INC, ...
use lib "C:/Cygwin/tmp/mylib";

my $func = "func.inc";

do $func;

# Now we can just call it. Note that with strict subs enabled,
# we have to use parentheses. We could also predeclare with
# use subs qw/ hello /; 
hello();

# do places func.inc's location in %INC
if ($INC{$func}) {
  print "$0: $func found at $INC{$func}\n";
}
else {
  die "$0: $func missing from %INC!";
}

Его вывод

Hello, world!
./prog: func.inc found at C:/Cygwin/tmp/mylib/func.inc

Как вы заметили, do не всегда не кристаллическая лестница, что объясняется в документации do:

Если do не может прочитать файл, он возвращает undef и устанавливает $! на ошибку. Если do может прочитать файл, но не может скомпилировать его, он возвращает undef и устанавливает сообщение об ошибке в $@. Если файл успешно скомпилирован, do возвращает значение последнего вычисленного выражения.

Чтобы проверить все эти случаи, мы больше не можем просто использовать do "func.inc", а

unless (defined do $func) {
  my $error = $! || $@;
  die "$0: do $func: $error";
}

Пояснения для каждого случая приведены ниже.

do не может прочитать файл

Если мы переименуем func.inc в nope.inc и перезапустим программу, мы получим

./prog: do func.inc: No such file or directory at ./prog line 12.

do может прочитать файл, но не может его скомпилировать

Переименуйте nope.inc обратно в func.inc и удалите закрывающую фигурную скобку в hello, чтобы она выглядела как

sub hello {
  print "Hello, world!\n";

1;

Запустив программу сейчас, мы получим

./prog: do func.inc: Missing right curly or square bracket at C:/Cygwin/tmp/mylib/func.inc line 4, at end of line
syntax error at C:/Cygwin/tmp/mylib/func.inc line 4, at EOF

do может прочитать файл и скомпилировать его, но он не возвращает истинное значение.

Удалите 1; в конце func.inc, чтобы сделать его

sub hello {
  print "Hello, world!\n";
}

Теперь вывод

./prog: do func.inc:  at ./prog line 13.

Таким образом, без возвращаемого значения успех напоминает провал. Мы могли бы усложнить код, проверяющий результат do, но лучший выбор - всегда возвращать истинное значение в конце библиотек и модулей Perl.

Обратите внимание, что программа работает правильно даже с включенной проверкой заражения (-T). Попробуйте и посмотрите! Обязательно прочитайте Taint mode и @INC в perlsec .

3 голосов
/ 10 января 2010

Вы используете подпрограмму так же, как и любую другую подпрограмму. Неважно, что вы загрузили его с do. Тем не менее, вы не должны использовать do для этого. Посмотрите главу «Пакеты» в Промежуточный Perl для подробного объяснения загрузки подпрограмм из других файлов. Короче говоря, используйте требуют вместо.

См. Документацию для do . Вам нужно иметь func.inc (который также можно просто назвать func.pl , поскольку pl - это "библиотека perl") в одном из каталогов, где Perl буду искать библиотеки. Это может отличаться от каталога, в котором есть index.pl . Поместите куда-нибудь func.inc в @INC или добавьте его каталог в @INC. do также не умирает, если не может загрузить файл, поэтому он не сообщает вам, что произошел сбой. Вот почему вы не должны использовать do для загрузки библиотек. :)

2 голосов
/ 10 января 2010

Убедившись в правильности пути, используйте:


#!/usr/bin/perl 
require("func.inc");
print "Content-type: text/html\n\n"; 
print foobar(); 
1 голос
/ 10 января 2010

Сначала я проверю, был ли файл действительно загружен, в документации для do упоминается, что он обновляет %INC, если файл был найден. Также есть больше информации в документации.

0 голосов
/ 10 января 2010

убедитесь, что у вас есть func.inc на правильном пути.

do "func.inc" 

означает, что вы говорите, что func.inc находится в том же пути, что и ваш Perl-скрипт. проверьте правильный путь и затем сделайте это

do "/path/func.inc"
...