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

I have a two part question really, relating to the following code.

#!perl.exe use strict; use warnings; my $var1 = "test.txt"; open TST, ">>$var1" or die do { &One(); &Two(); }; print TST "Another Line\n"; close TST; sub One { print "In Sub ONE\n"; } sub Two { print "In Sub TWO\n"; }

Firstly, I need to run 2 subroutines if my open fails, the code I have come up with works but seems clumsy. To test this I have created a test.txt file and made it read only. Is there a better way to tackle what I am trying above?

Secondly, when I run the above code I alse get 1 at C:\Dev\MiscTests\sub.pl line 8. after the print in the 2 subs. What does this mean?

Many thanks

-----
Of all the things I've lost in my life, its my mind I miss the most.

Replies are listed 'Best First'.
Re: Run 2 Subroutines after a die
by jarich (Curate) on Oct 03, 2002 at 07:17 UTC
    Try the following instead:
    unless(open TST, ">>$var1") { One(); Two(); exit; # or die "Failed to open $var1: $!"; } # rest of code here.
    Then, if open fails you'll execute all the code in the block and then exit, which is what you're trying to do.

    I alse get 1 at C:\Dev\MiscTests\sub.pl line 8
    die is a function that takes a string and exits the program printing that message out with the line number and script name. In this case die is getting the result of your do{} as its string and printing it: "1". The result of your do just happens to be the return value of your subroutine Two, which is 1. (print returns 1 if it printed, and if there is no explicit return, the value of the last line (of code) in a subroutine is the return value).

    If you try my suggestion from above, you won't get that any more.

    Hope it helps.

    jarich

    Short answer to below
    Yes, you're entirely correct. if( !cond ) is identical to unless( cond ). unless( cond ) { ...... } else { ......} is also legal (although there is no elsunless) but I'd encourage you to change any unless-then-else structures you might be tempted to write into if-then-else because that is much easier to read (mostly because that's what everyone else expects to see).

    I prefer unless because I find that sometimes I don't see the ! in if( !$foo ). Readability kinda depends on the reader. unless does take more character presses...

      Would I be correct in assuming that

      if(! open TST, ">>$var1"){ }

      would just be another way of doing

      unless(open TST, ">>$var1"){ }

      If so, is this just another way of doing things or are there further benefits for using unless. I did once read that the code we use should be readable to humans and that the computers will still interpret our code. Is this a case of unless being easier to read than if not?

      -----
      Of all the things I've lost in my life, its my mind I miss the most.

        Would I be correct in assuming that if(! open TST, ">>$var1"){ } would just be another way of doing unless(open TST, ">>$var1"){ }

        Yes, you would.

        If so, is this just another way of doing things or are there further benefits for using unless. I did once read that the code we use should be readable to humans and that the computers will still interpret our code. Is this a case of unless being easier to read than if not?

        unless (foo) is just another way of writing if (not (foo)). It's up to you to decide what you find better readable.

        - Yes, I reinvent wheels.
        - Spam: Visit eurotraQ.
        

      As a point of preference, I prefer the unless blocks also.. I generally use them in just such a situation, and besides not missing the ! in the !condition, when I see an unless block I know that I'm generally handling failure. I know... in all conditional blocks, you're acting upon something either 'succeeding' or 'failing', but when I'm specifically putting something in there that is mainly sort-of error checking things that I'm assuming (files exist and are openable, etc.), I'll use unless.

      -=rev=-
Re: Run 2 Subroutines after a die
by robartes (Priest) on Oct 03, 2002 at 07:15 UTC
    Hi AcidHawk,

    your output includes 1 at C:\Dev\MiscTests\sub.pl line 8 because that's what die does:

    die LIST;

    prints LIST and appends the location in the program if LIST does not end in a newline. In your case, LIST is the do block, which returns 1, hence 1 at ....

    Another way to call a number of functions or generally do stuff when dying, is to put said stuff in an END block:

    open HELLO ">hello.txt" or die "Blerch: $!\n"; END { print "Endings are a sad business.\n"; }

    Caveat: This may not be what you're looking for, as END blocks get executed whenever the program terminates, normally or abnormally. So they get executed even if your program completes normally and does not die anywhere.

    CU
    Robartes-

    Update: Added example code and caveat.

Re: Run 2 Subroutines after a die
by BrowserUk (Patriarch) on Oct 03, 2002 at 09:30 UTC

    As always, TMTOWTDI... the comma operator can be useful for things like this.

    #! perl -sw use warnings; use strict; my $var1 = "aBadFileName?"; open TST, ">>$var1" or One(), Two(), die "Couldn't open $var1; $!\n"; print TST "Another Line\n"; close TST; sub One { print "In Sub ONE\n"; } sub Two { print "In Sub TWO\n"; } __DATA__ C:\test>202447 In Sub ONE In Sub TWO Couldn't open aBadFileName?; Invalid argument C:\test>

    Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
Re: Run 2 Subroutines after a die
by bart (Canon) on Oct 03, 2002 at 09:37 UTC
    Firstly, I need to run 2 subroutines if my open fails, the code I have come up with works but seems clumsy.

    This looks like an appropriate spot to use $SIG{__DIE__}.

    { local $SIG{__DIE__} = sub { One(); Two(); die shift; }; open TST, ">>$var1" or die "Can't open file: $!"; }

    The code would also run if your script dies in the block for an unforeseen reason. Well, I can't imagine anything that could cause it in this simple example :-) but it's a useful technique for the toolbox.

Re: Run 2 Subroutines after a die
by Dog and Pony (Priest) on Oct 03, 2002 at 07:46 UTC
    This is one way of doing it (I assume your subs are cleanup routines rather than routines that should output stuff in the die message?):
    open TST, ">>$var1" or One(), Two(), die "Could not open '$var1': $!";
    If that is any clearer may be disputed... I think it is, but some may disagree. Maybe '&&' instead of commas are more intiutive between the calls? That works too.

    The '1' is, as pointed out, what do returns to your die for printing.

    Hope that helps.


    You have moved into a dark place.
    It is pitch black. You are likely to be eaten by a grue.
Re: Run 2 Subroutines after a die
by jmcnamara (Monsignor) on Oct 03, 2002 at 08:00 UTC

    In relation to your first question, the method that you are using seems fine.

    In relation to your second question, the 1 in the die message is returned by the print in sub Two and then returned by the do{}.

    To get a more useful error message you could do this:

    open TST, "$var1" or die do { One(); Two(); "Couldn't open file $var1: $!.\n"; };

    --
    John.

Re: Run 2 Subroutines after a die
by LEFant (Scribe) on Oct 03, 2002 at 16:58 UTC

    Doing things after we die is unattainable except for the most exaltred of our order. Doing the most essential things before we die should be our goal. It would seem to me that no more often that it would be called another sub might be wise:

    #!perl.exe use strict; use warnings; my $var1 = "test.txt"; open TST, ">>$var1" or scram($var1, $!); print TST "Another Line\n"; close TST; sub One { print "In Sub ONE\n"; } sub Two { print "In Sub TWO\n"; } sub scram{ my ($filename, $exitmsg ) = @_; One(); Two(); die "Error opening $filename: $exitmsg\n"; }
Re: Run 2 Subroutines after a die
by kabel (Chaplain) on Oct 03, 2002 at 19:14 UTC
    here is another way of doing things ;) unfortunately i was unable to implement it - it just did not work - but i know it is possible:

    the Fatal.pm module allows to make some function calls die if they return undef - handy for e.g. open etc. i thought i could do this for the die call for myself (for example, call two functions named One () and Two () before dieing :), looked at the modules source - the rest can be read above...

    perhaps one can put here the correct way of doing this? or is this way considered more harmful?
      ok here is another way that is not like the one i found in Fatal.pm, but it seems to work either.

      use strict; use warnings; use subs qw(die); # install my new die code sub die { One (); Two (); print "Die.\n"; CORE::die @_; } my $filename = "this file does not exist"; open (FH, $filename) or die "$filename: $!"; # do something close (FH); sub One { print "One . ";} sub Two { print "Two . ";}