How to override exit() call in Perl eval block

后端 未结 2 1209
醉梦人生
醉梦人生 2021-01-12 15:17

I need to eval some code in Perl that might some times contain an exit() call in it. A very simplified example of this would be:

us         


        
相关标签:
2条回答
  • 2021-01-12 16:04

    exit isn't meant to be trapped, so eval isn't the solution here. You could put the remaining code you need to run in an END block:

    some_function();
    END { print "Still alive! For now...\n"; }
    
    sub some_function {
        print "Hello from some_function\n";
        exit;
    }
    

    But if you absolutely, positively need to prevent exit from killing the script, you'll have to redefine exit() at compile time:

    BEGIN { *CORE::GLOBAL::exit = sub (;$) { } } # Make exit() do nothing
    some_function();
    print "Still alive!\n"; # Gets printed
    
    *CORE::GLOBAL::exit = *CORE::exit; # Restore exit()
    exit;
    print "I'm dead here.\n"; # Doesn't get printed
    
    sub some_function { exit }
    

    The Test::Trap module from the CPAN can be used to encapsulate this bit of ugliness for you, if you're interested in a more robust solution. Personally I would locally patch the exiting some_function() to use croak instead, and maybe file a bug report with the patch if it's a module.

    If you're just evaling user input and you don't want them to be able to call exit, verify that the string contains no calls to exit or to a subroutine that would indirectly exit, then eval it. Personally I'd be more afraid of unlink and fork than exit if the user is inputting arbitrary code to be evaluated.

    0 讨论(0)
  • 2021-01-12 16:05

    You can override exit, but you must do so at compile-time. So use a flag to signal whether the override is active or not.

    our $override_exit = 0;
    BEGIN { 
        *CORE::GLOBAL::exit = sub(;$) {
            die "EXIT_OVERRIDE\n" if $override_exit;
            CORE::exit($_[0] // 0);
        };
     }
    
     eval {
        local $override_exit = 1;
        some_function();
     };
    
     my $exit_was_called = $@ eq "EXIT_OVERRIDE\n";
     die $@ if $@ && !$exit_was_called;
    
     warn("Exit was called\n") if $exit_was_called;
    

    But that creates an exception that might be caught unintentionally. So let's use last instead.

    our $override_exit = 0;
    BEGIN { 
        *CORE::GLOBAL::exit = sub(;$) {
            no warnings qw( exiting );
            last EXIT_OVERRIDE if $override_exit;
            CORE::exit($_[0] // 0);
        };
     }
    
     my $exit_was_called = 1;
     EXIT_OVERRIDE: {
        local $override_exit = 1;
        eval { some_function() };
        $exit_was_called = 0;
        die $@ if $@;
     }
    
     warn("Exit was called\n") if $exit_was_called;
    

    Note that eval BLOCK is used to catch exception. eval EXPR is used to compile code.

    0 讨论(0)
提交回复
热议问题