I've had to deal with this from time to time. I also cover this in a PERL class that I teach at my company.

What I've learned, through various approaches, is that you want to depend on the result of open() (or sysopen()). A file might exist, for instance, because -e "file" returns true, but you still might not be able to open it (because, perhaps, you don't have read permissions for it, only list permissions on a UNIX system).

Also, if it's important to read the file, even when you can't write to it, then you should handle reading and writing separately (as shown below). If you must be able to do both, then don't bother testing for the read-only case -- open for read+update, and fail if that doesn't work.

So, when I read your problem statement, I'm left with the impression that you might be working with an INI file, which you'll create if missing, update if writable, use if read-only, and ignore if none of the above. (If that's not right, feel free to take or leave the following as you see fit).

Here's how I would go about it:
# load_ini # returns undef on error, hashref on success sub load_ini($) { my ($ini) = @_; # first, check for all of those special conditions # that you wanted to avoid, like directories, etc: return undef if ( -d $ini or ! -r $ini or ! -f $ini ); my $fh = new FileHandle($ini); return undef unless defined $fh; # read INI here, represent it in a data struct my $ini_data = parse_ini($fh); # return handy structure (could be an object) # that keeps the INI filename in it for later use return { ini => $file, data => $ini_data }; } # load_ini_safe # same as load_ini, but never "fails" sub load_ini_safe($) { my ($ini) = @_; # provide a default if load_ini() fails # This would prevent you from saving to the INI file # if we load defaults. But see below... # return load_ini($ini) # or { ini => undef, ini_data => default_ini() }; # This will allow you to SAVE the ini settings if you # ONLY have write permissions... (yeah, odd case, # that). return load_ini($ini) or { ini => $ini, ini_data => default_ini() }; } # save_ini # returns boolean 1 on success, 0 on failure sub save_ini($) { my ( $ini_struct ) = @_; # this is a hash slice, btw... my ( $ini, $ini_data ) = %{ $ini_struct }{ qw/ini ini_data/ }; # check for a valid filename # (could be undef, see load_ini_safe for details) return 0 unless $ini; # If we keep the filename around from load_ini_safe, # then we have to perform the same checks as in # load_ini(). # If we only keep the filename if we could open it, # then we don't need to perform these tests return 0 if ( -d $ini or ! -r $ini or ! -f $ini ); # Now, try to open the file for writing. my $fh = new FileHandle(">$ini"); return 0 unless defined $fh; # now write the contents out to the filehandle # presumably, this returns 1 on success, 0 on failure return write_ini( $fh, $ini_data ); } # get_default_ini_data # provide default INI hashref sub default_ini() {} # parse_ini # converts text from filehandle into INI hashref sub parse_ini($) {} # write_ini # writes in-memory INI data structure to file as text sub write_ini($\%) {}
So, depending on what you want (i.e. load_ini or load_ini_safe), you could keep those separate or combine them or get rid of load_ini_safe. I broke them up to show a division of labor, since I was unclear about just which steps were important to you. Note that this has a small advantage over opening a file in read+update mode -- if it's read-only, then you can load it. If it's not writable, then writing fails (maybe silently in your app). If you only have write permissions for the file, you could load defaults internally and write those out (not very useful, but there it is). You get to completely avoid the error handling for trying to open a file for read+update when loading and/or saving, which would fail for both the load and the save cases if the file were read-only or write-only. Hope this helps... :)

In reply to Re: file testing is hard by dpmott
in thread file testing is hard by jamgill

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.