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

Fellow monks,

I'm not sure if this SOPW or a Meditation since it's somewhere inbetween but here goes.

I'm in the process of writing a cgi program that is going to look for a user's config file. The file is going to be made up of the user's selected items selected from a large number of checkboxes.If said file doesn't exist, that's not a problem as it's going to create one. If the file does exist then I wanted to be able to recreate the checkbox page with the current selections already checked. So far no problem.

My question is regarding die. I've always seen code such as:

open FH, ">user1.cfg" or die "Couldn't open file;

but in my case that isn't going to apply.

I really don't care if the file doesn't exist initially since I'm going to build it if it doesn't.

Hence my question: Should you always die after a file not found or is that a bad coding practice?

There is no emoticon for what I'm feeling now.

Replies are listed 'Best First'.
•Re: To die or not to die
by merlyn (Sage) on Oct 28, 2002 at 18:43 UTC
    You don't really want death, but you do want to check the return value. Use logic like this:
    my %config; if (open FH, "user.config.file") { # we have a previous save ... get %config from FH ... close FH; } elsif ($! !~ /no such file/i) { die "config file: $!"; } ... now use %config ...
    This way, if the previous config is found, it's used. Otherwise, if it's not because it's absent, we die. Simple logic.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: To die or not to die
by nothingmuch (Priest) on Oct 28, 2002 at 18:40 UTC
    You shouldn't always die - you should always be prepared. The open you specified will empty any existing file, or create an empty one if one doesn't exist. The open will fail if you haven't enough permissions, or something like that. If, however, you do something like
    open FH,"user1.cfg" or make_new_conf("user1.cfg");
    Then you'd deal with the inexistant file problem you think you needn't deal with. Then, should the file be inexistant - the open will also fail, making a new conf file instead (provided you have defined such a sub). If other errors occur, the same sub will also be called)

    Always be prepared for errors - you don't need to fatalise them, you don't need to even report them - just don't count on system calls to always succeed, as they may not.

    Update: dyslexia strikes again - it seemed clear when i reviewed it... =P sorry.

    -nuffin
    zz zZ Z Z #!perl
Re: To die or not to die
by broquaint (Abbot) on Oct 28, 2002 at 18:41 UTC
    I really don't care if the file doesn't exist initially since I'm going to build it if it doesn't.
    While you may wish to build the file, it may not be so. Anything from incorrect permissions to a lack of disk space could get in your way, and this is when a judicious use of die() comes in handy. So when things aren't working you'll find out pretty quickly.
    HTH

    _________
    broquaint

Re: To die or not to die
by BrowserUk (Patriarch) on Oct 28, 2002 at 21:35 UTC

    You could always check if the file exists before trying to open it, and use that to decide whether to open it for input or output. That way, you know that if it exists but you can't open it for input, the error is serious enough to warrent a die (or warn depending on your error handling).

    if ( -e $file ) { open FH, $file or die "Failed: $! trying open existing $file\ +n"; # read it open FH, '>', $file or die "Failed: $! trying to re-open $file\n"; + } else { open FH, '>'.$file or die "Failed: $! trying to create new $file\ +n"; # do stuff }

    This way, if you get out of the if/else, you know you have a file open for output ready for (re)writing.

    It also means that you get a meaningful error msg if the file is read-only.


    Nah! Your thinking of Simon Templar, originally played by Roger Moore and later by Ian Ogilvy
      It probably won't matter here, but that's a bad idea in general since you open up yourself to a race condition: the file might be created after the -e test but before the open by another process. You need an atomic operation:
      if (open FH, "<", $file) { # read it and do stuff open FH, ">", $file or die "Failed: $! trying to re-open $file\n"; } else { # do stuff open FH, ">", $file or die "Failed: $! trying to create new $file +\n"; }
      Also, use the three-argument form of open as I've said elsewhere in this thread.

      Makeshifts last the longest.

Re: To die or not to die
by Mr. Muskrat (Canon) on Oct 28, 2002 at 18:45 UTC
    open FH, ">user1.cfg" or die "Couldn't open file;"

    It does not matter if the user1.cfg exists prior to running the code. If it does not exist, it will be created. If it does exist, it will be overwritten. If you are unable to open the file for writing, you should not proceed with writing to the file. You will get errors when you attempt to write to a file that hasn't been opened for writing.

      You're right. After thinking about what you had said, I should have put:

      open FH, "user1.cfg" not ">user1.cfg"

      Well at least that is what I was trying for initially.

      There is no emoticon for what I'm feeling now.

        You should better have said open FH, "<user1.cfg" or, really, open FH, "<", "user1.cfg". Make it a habit of using the three-argument form of open - esp since I suspect that in this case, your filename will not be hardwired but dynamic and maybe even derived from user input. In that case, the two-argument form may leave you open to a range of problems from "simple" bugs to outright security holes.

        Makeshifts last the longest.

Re: To die or not to die
by SpaceAce (Beadle) on Oct 28, 2002 at 18:58 UTC
    You might want to write a generic reporting sub. I have a standard package of useful routines I use again and again in my programs and one of them is a subroutine that prints to STDOUT allows you to exit or not exit as you see fit.

    Ie, open(INPUT, "<input.txt") || NoGo(error=>"Could not open file input.txt for reading", exit=>1);

    That way you can report errors to yourself or the user and terminate the program if necessary with a minimum of hassle. This is best if you are only worried about success or failure and not the exact cause.

    SpaceAce

      If you didn't want to die, and didn't want to use merlyn's example, you could always "warn" instead of die... ex

      open(INPUT, "input.txt") or warn("Could not open file input.txt $!\n");

      Also, note that on the open(), the file is "input.txt" and not ">input.txt" as this will open the file for writing, destroying anything that might already be in the file and would only die or warn if the file could not be created (low disk space, permissions, etc).

      One last thing, what's the difference between or and || in die or warn statements? I know one is numeric and the other string, but as far as usage in this example, is there a difference or purely syntatic sugar?

      Thanks
      --
      Ben
      "Naked I came from my mother's womb, and naked I will depart."

        you could always "warn" instead of die
        ... except that you'd then be warning even on "expected" conditions. Earlier in this thread it was said that having no config file was a reasonable and expected condition.

        If you have warnings disabled, you could do:

        open F, "config.txt" or $! !~ /no such file/i or die "config.txt error +: $!"; ... process F ...; close F;
        Warnings will have to be turned off because you'll be doing I/O on a closed filehandle. My other code posted earlier doesn't have that issue.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

Re: To die or not to die
by Popcorn Dave (Abbot) on Oct 28, 2002 at 21:18 UTC
    Thank you one and all for not only helping with my question but for furthering my ever expanding knowledge of Perl!

    There is no emoticon for what I'm feeling now.

Re: To die or not to die
by Aristotle (Chancellor) on Oct 29, 2002 at 21:03 UTC
    I'll assume you use an array to store the defaults. Then I'd say something like:
    chomp(my @checked_names = do { open my $fh, "<", $config_file ? (<$fh>) : $! =~ /no such file/i ? () : die "Can't open $config_file to read: $!"; }); # ... do stuff with @checked_names open my $fh, ">", $config_file or die "Can't open $config_file to writ +e: $!"; print $fh map "$_\n", @checked_names; close $fh;

    Note the use of a lexical to hold the filehandle in the do block. This means the file will be auto-closed when the $fh goes out of scope at the end of the block: otherwise, we'd have to follow the ternary with a close, forcing us to store the file content in a temporary array to be able to return it.

    Although from the looks of this, since the file is reopened for writing later anyway, there may not be much incentive to die on the read attempt even if the error was due to something other than a nonexistant file. YMMV

    Update: merlyn is right, of course.

    Makeshifts last the longest.

      Note the use of a lexical to hold the filehandle in the do block.
      So noted.

      But I don't see "require 5.006;" at the top of your code, because you just made it non-portable for 5.005 sites.

      I hope you're aware of that. If not, you are now. And so is anyone else reading this thread.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

Re: To die or not to die
by Molt (Chaplain) on Oct 29, 2002 at 10:58 UTC

    Not quite sure what you mean by the 'I really don't care if the file doesn't exist initially since I'm going to build it if it doesn't'. When a file is opened with the > prefix you're opening it to write to it, and any file already there of that name will be blanked anyway. When this open fails it means you can't build the file there, maybe you don't have write permissions, or maybe the filesystem is completely full.

    If you're planning on building a file and open ">file" fails then you do have problems since you can't actually write to the file and dying is probably a good plan.