Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

comment on

( #3333=superdoc: print w/replies, xml ) Need Help??

open Best Practices

TL;DR: open my $fh, '<', $filename or die "$filename: $!";

You will see styles of open such as "open FILE, $filename;" or "open(LOG, ">$filename") || die "Could not open $filename";" in many places. These mainly come from versions of Perl before 5.6.0 (released in 2000), because that version of Perl introduced lexical filehandles and the three-argument open. Since then, these new features have become a best practice, for the reasons below.

1. Use Lexical Filehandles

Instead of open FILE, ..., say: open my $fh, ....

Lexical filehandles have the advantage of not being global variables, and such filehandles will be automatically closed when the variable goes out of scope. You can use them just like any other filehandle, e.g. instead of print FILE "Output", you just say print $fh "Output". They're also more convenient to pass as parameters to subs. Also, "bareword" filehandles like FILE have a potential for conflicts with package names (see choroba's reply for details).

2. Use the Three-Argument Form

Instead of open my $fh, ">$filename", say: open my $fh, '>', $filename.

In the two-argument form of open, the filename has to be parsed for the presence of mode characters such as >, <+, or |. If you say open my $fh, $filename, and $filename contains such characters, the open may not do what you want, or worse, if $filename is user input, this may be a security risk! The two-argument form can still be useful in rare cases, but I strongly recommend to play it safe and use the three-argument form instead.

In the three-argument form, $filename will always be taken as a filename. Plus, the mode can include "layers", so instead of having to do a binmode after the open, you can just say e.g. open my $fh, "<:raw", $filename, or you can specify an encoding such as open my $fh, ">:encoding(UTF-8)", $filename.

3. Check and Handle Errors

open my $fh, '<', $filename;                          # Bad: No error handling!
open my $fh, '<', $filename  || die ...;              # WRONG!1
open my $fh, '<', $filename  or die "open failed";    # error is missing info

open my $fh, '<', $filename  or die "$filename: $!";  # good
open(my $fh, '<', $filename) or die "$filename: $!";  # good
open(my $fh, '<', $filename) || die "$filename: $!";  # works, but risky!1

use autodie qw/open/;  # at the top of your script / code block
open my $fh, '<', $filename;                          # ok, but read autodie!

You should check the return value of the open function, and if it returns a false value, report the error that is available in the $! variable. It is best to also report the filename as well, and of course you're free to customize the message as needed (see the tips below for some suggestions).

1 It is a common mistake to use open my $fh, '<', $filename || die ... - because of the higher precedence of ||, it actually means open( my $fh, '<', ($filename || die ...) ). So to avoid mistakes, I would suggest just staying away from || in this case (as is also highlighted in these replies by AM and eyepopslikeamosquito).

Note that open failing does not necessarily have to be a fatal error, see some examples of alternatives here. Also, note that the effect of autodie is limited to its lexical scope, so it's possible to turn it on for only smaller blocks of code (as discussed in kcott's reply).

4. Additional Tips

  • Make sure that the filename you're opening always matches the filename in the error message. One easy way to accomplish this is to use a single variable to hold the filename, like $filename in the above examples (as described in Eily's reply).
  • Consider putting the filename in the error message in quotes or similar, such as "'$filename': $!", so that it's easier to see issues arising from whitespace at the beginning or end of the filename (as suggested in Discipulus's reply).
  • In addition, consider adding even more useful details to your error message, such as whether you're trying to read or write from/to the file, and put quotes around $! as well, so it's easier to tell everything apart (as suggested by haj).
  • On Windows, consider also displaying $^E as part of the error message for more information (as suggested in Discipulus's reply).
  • If you're setting global variables that will affect reading the file, like $/, it's best to use local in a new block (as mentioned in stevieb's reply).
  • Remember that it's possible for multiple processes to access the same file at the same time, and you may need to consider a way to coordinate that, such as file locking (as mentioned in davido's reply).

Fellow Monks: I wrote this so I would have something to link to instead of repeating these points again and again. If there's something you think is worth adding, please feel free to suggest it!

Update 2019-07-12: Added section "Additional Tips", mentioned bareword filehandles, and added a bit more on autodie. Thanks to everyone for your suggestions! 2019-07-13: Added more suggestions from replies, thanks!

In reply to "open" Best Practices by haukex

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, 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, 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.
  • Log In?

    What's my password?
    Create A New User
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others lurking in the Monastery: (3)
    As of 2020-02-24 00:42 GMT
    Find Nodes?
      Voting Booth?
      What numbers are you going to focus on primarily in 2020?

      Results (104 votes). Check out past polls.