2009-12-16 10 views
24

Tôi cần bao gồm động mô-đun Perl, nhưng nếu có thể muốn tránh xa eval do các tiêu chuẩn mã hóa công việc. Công trình này:Làm cách nào để tự động bao gồm mô-đun Perl mà không sử dụng eval?

$module = "My::module"; 
eval("use $module;"); 

Nhưng tôi cần cách để làm điều đó mà không cần eval nếu có thể. Tất cả các tìm kiếm trên google đều dẫn đến phương thức eval, nhưng không có cách nào khác.

Có thể thực hiện việc này mà không cần eval không?

+1

Trùng lặp: http://stackoverflow.com/questions/442710/how-do-i-use-a-perl-package-known-only-in-runtime –

+0

Xin lỗi về điều đó. Tôi đã tìm kiếm nhưng không thể ' – user226723

+2

Tôi nhận được các tiêu chuẩn mã hóa của bạn được cập nhật. Không có gì sai hoặc không an toàn về việc đánh giá những gì cơ bản được mã hóa cứng chuỗi. Đó là cách đơn giản nhất để hoàn thành những gì bạn muốn. Nếu bạn đang chuyển vào đầu vào của người dùng, đó là một câu chuyện khác ... – runrig

Trả lời

43

Sử dụng require để tải mô-đun khi chạy. Nó thường là một ý tưởng tốt để bọc này trong một khối (không phải chuỗi) eval trong trường hợp không thể tải mô-đun.

eval { 
    require My::Module; 
    My::Module->import(); 
    1; 
} or do { 
    my $error = [email protected]; 
    # Module load failed. You could recover, try loading 
    # an alternate module, die with $error... 
    # whatever's appropriate 
}; 

Lý do cho sự eval {...} or do {...} cú pháp và thực hiện một bản sao của [email protected] là vì [email protected] là một biến toàn cầu có thể được thiết lập bởi nhiều điều khác nhau. Bạn muốn lấy giá trị như nguyên tử nhất có thể để tránh tình trạng cuộc đua, nơi một cái gì đó khác đã đặt nó thành một giá trị khác.

Nếu bạn không biết tên của các mô-đun cho đến khi thời gian chạy bạn sẽ phải làm bản dịch giữa tên mô-đun (My :: Module) và tên file (My/Module.pm) bằng tay:

my $module = 'My::Module'; 

eval { 
    (my $file = $module) =~ s|::|/|g; 
    require $file . '.pm'; 
    $module->import(); 
    1; 
} or do { 
    my $error = [email protected]; 
    # ... 
}; 
+0

Hmm. Điều này là tốt bởi vì bạn bọc các yêu cầu trong một eval, nhưng yêu cầu của bạn không phải là đặc biệt năng động. Nếu ai có thể hợp nhất câu trả lời, Dan và của bạn sẽ là ứng cử viên hàng đầu của tôi. – innaM

+0

Trong khi yêu cầu của ông không phải là năng động, tôi đã dễ dàng có thể làm cho nó như vậy (như Picard sẽ nói). Cảm ơn câu trả lời! – user226723

+0

Đây không phải là rất an toàn, ví dụ: xem http://www.perlfoundation.org/perl5/index.cgi?exception_handling – Ether

10

Vâng, có luôn require là như trong

require 'My/Module.pm'; 
My::Module->import(); 

Lưu ý rằng bạn bị mất bất cứ điều gì hiệu ứng mà bạn có thể đã nhận từ import được gọi tại thời gian biên dịch thay vì chạy.

Chỉnh sửa: Sự cân bằng giữa giá trị này và eval là: eval cho phép bạn sử dụng cú pháp mô-đun bình thường và cung cấp cho bạn một lỗi rõ ràng hơn nếu tên mô-đun không hợp lệ (trái với chỉ đơn thuần là không tìm thấy). OTOH, cách eval là (có khả năng) nhiều hơn tùy thuộc vào việc tiêm mã tùy ý.

+0

Cảm ơn câu trả lời. Tôi đã bình chọn bạn vì tôi không thể chọn cả hai câu trả lời :). – user226723

4

Không, không thể không có eval, vì require() cần tên mô-đun thanh viên, như được mô tả tại perldoc -f require. Tuy nhiên, nó không phải là một sử dụng ác của eval, vì nó không cho phép tiêm mã tùy ý (giả sử bạn có quyền kiểm soát nội dung của tập tin bạn đang require ing, tất nhiên).

EDIT: Mã được sửa đổi bên dưới, nhưng tôi sẽ rời phiên bản đầu tiên để hoàn chỉnh.

tôi sử dụng tôi sử dụng để sử dụng module này ít đường để làm tải trọng động trong thời gian chạy:

package MyApp::Util::RequireClass; 

use strict; 
use warnings; 

use Exporter 'import'; # gives you Exporter's import() method directly 
our @EXPORT_OK = qw(requireClass); 

# Usage: requireClass(moduleName); 
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports); 
sub requireClass 
{ 
    my ($class) = @_; 
    eval "require $class" or do { die "Ack, can't load $class: [email protected]" }; 
} 

1; 

PS. Tôi đang nhìn chằm chằm vào định nghĩa này (tôi đã viết nó khá lâu trước đây) và tôi đang cân nhắc việc thêm điều này: $class->export_to_level(1, undef, @imports); ... nó nên hoạt động, nhưng không được kiểm tra.

EDIT: phiên bản 2 bây giờ, đẹp hơn nhiều mà không có một eval (nhờ ysth): :)

package MyApp::Util::RequireClass; 

use strict; 
use warnings; 

use Exporter 'import'; # gives you Exporter's import() method directly 
our @EXPORT_OK = qw(requireClass); 

# Usage: requireClass(moduleName); 
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports); 
sub requireClass 
{ 
    my ($class) = @_; 

    (my $file = $class) =~ s|::|/|g; 
    $file .= '.pm'; 
    require $file; # will die if there was an error 
} 

1; 
+3

"require() cần tên mô-đun bareword" - không, nó không; chỉ s # :: #/# g; và nối '.pm' để lấy tên đường dẫn. – ysth

+0

@ysth: wow, bạn nói đúng ... các tài liệu rất mơ hồ về điều này nhưng nó thực sự hoạt động. Vâng đó là lộng lẫy, bây giờ tôi có thể sửa đổi mô-đun của tôi để loại bỏ cái ác eval :) – Ether

16

Cách sử dụng mô đun lõi Module::Load

Với ví dụ:

"Mô-đun :: Tải - thời gian chạy yêu cầu của cả hai mô-đun và tệp"

"tải loại bỏ sự cần thiết phải biết bạn đang cố yêu cầu tệp hay mô-đun."

Nếu nó không thành công nó sẽ chết với một cái gì đó tương tự "Không thể xác định vị trí xxx trong @ INC (@ INC chứa: ...".

+1

Đó là một mô-đun lõi, vì vậy nên được sử dụng. – alexk

0

tôi thích làm những việc như ..

require Win32::Console::ANSI if ($^O eq "MSWin32");