2011-09-29 26 views
15

Những cạm bẫy phổ biến được kết hợp với số eval của Perl, điều gì có thể khiến bạn chọn sử dụng mô-đun như Try::Tiny?Những cạm bẫy phổ biến khi sử dụng eval của Perl là gì?

+0

thể trùng lặp của [Tại sao '$ @ 'không đáng tin cậy?] (http://stackoverflow.com/questions/3686857/why-is-untrustworthy), [Có gì bị hỏng về ngoại lệ trong Perl?] (http://stackoverflow.com/questions/2165161/) – mob

+2

Chỉ lại ason bạn sẽ không sử dụng nội trang là bạn không chạy phiên bản hiện tại của Perl. – tchrist

+0

@mob - vâng, có vẻ như đó là cùng một câu hỏi. – Hugh

Trả lời

25

Perl của eval đến trong hai mùi vị, chuỗi eval và khối eval. String eval gọi trình biên dịch để thực thi mã nguồn. Block eval bao quanh mã đã được biên dịch trong trình bao bọc sẽ bắt ngoại lệ die. (chuỗi eval cũng bắt được ngoại lệ die cũng như bất kỳ lỗi biên dịch nào).

Thử :: Tiny chỉ áp dụng cho dạng khối của eval, nhưng những điều sau áp dụng cho cả hai biểu mẫu.

Mỗi lần bạn gọi eval, nó sẽ thay đổi giá trị [email protected]. Nó sẽ là '' nếu eval đã thành công hoặc lỗi do eval đánh bắt.

Điều này có nghĩa là bất kỳ khi nào bạn gọi eval, bạn sẽ xóa mọi thông báo lỗi trước đó. Try::Tiny bản địa hóa biến số [email protected] cho bạn, sao cho một eval thành công sẽ không xóa thông điệp của lần đánh bại trước đó thất bại.

Sự cố khác xuất phát từ việc sử dụng [email protected] làm séc để xác định xem eval có thành công hay không. Một mô hình phổ biến là:

eval {...}; 
if ([email protected]) { 
    # deal with error here 
} 

này dựa trên hai giả định, đầu tiên mà bất kỳ thông báo lỗi [email protected] có thể chứa là một giá trị đúng (thường là true), và rằng không có mã giữa khối eval và tuyên bố nếu.

Tất nhiên, điều này là đúng, nhưng nếu khối eval tạo một đối tượng và đối tượng đó không nằm trong phạm vi sau khi eval thất bại, thì phương thức DESTROY của đối tượng sẽ được gọi trước câu lệnh if. Nếu DESTROY xảy ra để gọi eval mà không cần bản địa hóa [email protected] và thành công, thì vào thời điểm phát biểu if của bạn, biến số [email protected] sẽ bị xóa.

Các giải pháp cho những vấn đề này là:

my $return = do { 
    local [email protected]; 
    my $ret; 
    eval {$ret = this_could_fail(); 1} or die "eval failed: [email protected]"; 
    $ret 
}; 

phá vỡ dòng cách nhau một dòng, local [email protected] tạo ra một mới [email protected] cho do khối mà ngăn chặn clobbering giá trị trước đó. my $ret sẽ là giá trị trả về của mã được đánh giá. Trong khối eval, $ret được gán cho, và sau đó khối trả về 1. Bằng cách đó, không có vấn đề gì, nếu eval thành công nó sẽ trả về true, và nếu nó không thành công nó sẽ trả về false. Đó là vào bạn những gì phải làm trong trường hợp thất bại. Đoạn mã trên chỉ chết, nhưng bạn có thể dễ dàng sử dụng giá trị trả về của khối eval để quyết định chạy mã khác.

Vì câu chú giải ở trên hơi tẻ nhạt, nó trở nên dễ bị lỗi.Sử dụng mô-đun như Try::Tiny cách ly bạn khỏi những lỗi tiềm ẩn đó, với chi phí của một vài cuộc gọi hàm trên mỗi eval. Điều quan trọng là phải biết cách sử dụng eval đúng cách, bởi vì Try::Tiny sẽ không giúp bạn nếu bạn phải sử dụng chuỗi eval.

+4

Cố định trong bản phát hành hiện tại. – tchrist

+0

Chỉnh sửa nhỏ - '$ @' sẽ thực sự là một chuỗi rỗng thay vì undef nếu không có ngoại lệ nào được ném. –

+1

@Grant McLean => cảm ơn, tôi đáng lẽ phải nhớ, vì đó là cách giao dịch repl thông thường của tôi với lỗi: 'perl -wE 'nói eval, $ @ trong khi <>'' –

6

Ngoài các câu trả lời ở trên, tôi sẽ thêm ...

  • eval bị ảnh hưởng bởi $SIG{__DIE__} xử lý gây ra hành động toàn cầu ở một khoảng cách.
  • Thật dễ dàng cho một người mới gây nhầm lẫn eval BLOCKeval STRING, vì chúng dường như làm điều tương tự, nhưng một là một lỗ hổng bảo mật.

Hãy thử :: Tiny có những cạm bẫy riêng của mình, lớn nhất là trong khi nó trông giống như một khối nó thực sự là một cuộc gọi chương trình con. Điều đó có nghĩa này:

eval { 
    ...blah blah... 
    return $foo; 
}; 

và điều này:

try { 
    ...blah blah... 
    return $foo; 
}; 

không làm điều tương tự. Chúng được trình bày trong số CAVEATS section of the Try::Tiny docs. Điều đó nói rằng, tôi muốn giới thiệu nó trên eval.

+2

Bạn đang nói rằng 'eval' là * vẫn * bất ngờ bị phá vỡ trong 5,14? ** Thật sao? ** Đó sẽ là * cực kỳ * đáng thất vọng, bởi vì tôi biết rất nhiều công việc đã đi vào nó để cố gắng làm cho 'Try :: Tiny' lỗi thời bằng cách sửa chữa bất cứ lỗi nào nằm bên dưới' eval'.Nếu nỗ lực đó thất bại, thì có một vấn đề khủng khiếp vì 'Try :: Tiny' vẫn chỉ là một mô-đun CPAN, không phải là cốt lõi. Nếu bạn không thể làm công việc thực sự, và đáng tin cậy, với cốt lõi, thì đó là một tình huống không thể chấp nhận được. – tchrist

+1

@tchrist Quên về điều đó. Như tôi đã hiểu, 5.14.0 đã sửa một lớp lỗi liên quan đến việc phá hủy đối tượng giữa $ @ và đối tượng và thường được tạo thành 'eval {...}; nếu ($ @) {...} 'đáng tin cậy hơn. Tôi tin rằng giải quyết 2 trong 3 điều Hãy thử :: Sửa lỗi nhỏ ... và thứ ba (sai $ @) là khá khó xảy ra. Nó vẫn để lại những điểm tôi đã đề cập. Nó sẽ là một tính năng 5.16 tốt đẹp để ngăn chặn '$ SIG {__ DIE __}' khỏi bắn bên trong một eval. Và quay xuống phim, anh chàng. – Schwern

+0

Gọi 'eval STRING' là“ vấn đề an ninh ”không chỉ là quá kịch tính; nó thậm chí không đúng. Tôi đã sử dụng 'eval STRING' vì nó xuất hiện lần đầu tiên trong perl2 khoảng hai mươi ba năm trước, tôi có thể đảm bảo với bạn rằng không bao giờ tôi gặp phải bất kỳ cái gọi là“ vấn đề an ninh ”nào với nó. Chắc chắn, lập trình câm có thể làm những điều ngu ngốc với nó, nhưng điều này là đúng của gần như bất cứ điều gì. Nếu bạn tồn tại trong một thế giới hoang tưởng về bệnh lý, bạn nên sử dụng chế độ taint và/hoặc ngăn an toàn. Trong một thế giới bình thường, 'eval STRING' nhận được rất nhiều công việc hữu ích được thực hiện; xem chương trình * đổi tên * cổ điển. – tchrist

0

Sử dụng hàm eval trên hàm X11 có thể vẫn không hoạt động.

Mã này được như

eval {  
    @win_arrays = GetWindowsFromPid($pid); 
}; 

Các kịch bản sẽ được thoát khỏi Lỗi

X đề nghị thất bại: ...

Các vấn đề liên quan