DrWhy has asked for the wisdom of the Perl Monks concerning the following question:

I have a script where I need to indicate success or failure via the exit value. However, I'm running into the problem that anything that sets's $? in END block overwrites your requested. There's a nice succinct discussion of this problem here (Beware $? and END).

It even includes the solution of making $? local in your END block. This won't work for me for the following reasons. I don't know where the END block is. It's buried deep in some library somewhere. This script uses currently over 100 libraries and that number is likely to grow over time. I also don't know if it's in only one place or multiple places so even if I did find one there might be others. The actual number of libraries used isn't even determined within the script itself. This script runs all the test scripts for our project (implemented within the Test::Unit framework) so as we add more modules and more tests for existing modules this is expected to grow. The END block (some of the END blocks) may well be in one of the CPAN or non-local, non-CPAN modules our modules make use of. If they aren't now then in the future one might be.

I tried to use Manip::END. Using this to push a new END block onto the list of END blocks that preserves/restoreds the desired exit value would work and be a nice local solution to this very non-local problem. But we're using 5.6.1 and Manip::END requires 5.8.0.

Anybody out there have any idea how I can get a local solution that works with 5.6.1? For instance, is it possible to get at the list of END block subroutines without Manip::END?

Much thanks!

-- DrWhy

  • Comment on Getting around $? setting in END blocks

Replies are listed 'Best First'.
Re: Getting around $? setting in END blocks
by cfreak (Chaplain) on Aug 26, 2004 at 02:39 UTC

    I read up on some of the documentation on END and I think the answer to your problem is pretty simple. According to perlmod: END {} blocks are executed in reverse order. The last END created is the first END that gets executed. It seems all you need to do is define your END {} at the very beginning of your program before the other ones get defined. That should allow you to set your $? variable to whatever you want since by the time its seen all of the modifications to it should have already happened.

    use is really just a BEGIN {} block and BEGIN executes in the order its seen. You could create a module that has your final end block and use it the very first. Tell it to redefine $? to some other value. use all of your other modules then at the end of all that use another module that creates an END { } that saves the original exit value from the program. Something like this:

    #!/usr/bin/perl use strict; use vars qw($myexitvalue); use MyLastEnd; use Foo; use Bar; use SomeOtherModule; use MyFirstEnd; # rest of script #MyLastEnd.pm END { $? = $myexitvalue; } #MyFirstEnd.pm END { $myexitvalue = $?; }

    All of this is untested of course, but if I'm reading the docs right it *should* work. Hope that helps!

      Can you say "DOH!"?

      Of course the "last" END block has to be compiled before the use statements get processed -- it's end blocks in use-d items that are giving me the headache. So you need:

      use vars qw($exitval); END {$? = $exitval} use LotsOStuff; use MoreStuff; use TheOtherGuy'sStuff; use StuffYouProbablyDontNeed; # your code here END {$exitval = $?}
Re: Getting around $? setting in END blocks
by ikegami (Patriarch) on Aug 26, 2004 at 05:21 UTC
    END blocks are executed in the reverse order they are encountered. So if you put an END block right at the top of your program that sets $?, you should be set.
    my $rv = 2; # assume fatal. END { $? = $rv; } use ... use ... { ... if (...) { $rv = 1; exit(); } ... $rv = 0; }
Re: Getting around $? setting in END blocks
by PodMaster (Abbot) on Aug 26, 2004 at 04:57 UTC
    But we're using 5.6.1 and Manip::END requires 5.8.0.
    I'm not so sure (in fact, i'm pretty sure it will work on 5.6.x). Yes, the Makefile.PL says use 5.008;, but the module does not, which leads me to believe that the use 5.008; in the Makefile.PL is just a h2xs artifact the author forgot to remove (and i'm right). This is a typical problem.

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      I think you're right (I can't remember) and I never tested it on anything else as it was more a proof of concept than a serious module. That said, it does work.

      A new version should be on CPAN by the time you read this. It removes the 5.8 dependency and also fixes a small bug in the error reporting, was it you who sent me the patch on RT?

      As for the original question, the best way to add an END block is by writing an END block! If you want it to be triggered before all the others, put it at the end of your program, if you want it to be triggered after all the others put it at the start, before any use or require . A combination of the two can solve your problem.

      my $saved_value; END { $? = $saved_value } # this will be the final END block #### your program goes here #### using whatever modules you want END { $saved_value = $? # } # this will be the first END block
      The only thing that could go wrong with this scheme is that something could pull in a module containing another END block using require or eval "use something" and that END block could fiddle with $? before you get to save it. If this is happening you could try calling my_exit instead where
      sub my_exit { my $value = shift; $saved_value = $value; exit $value; }
Re: Getting around $? setting in END blocks
by ambrus (Abbot) on Aug 26, 2004 at 14:06 UTC
Re: Getting around $? setting in END blocks
by adrianh (Chancellor) on Aug 26, 2004 at 13:12 UTC

    In addition to the solutions already offered, if you don't care whether the END blocks run there is always POSIX::_exit.