http://www.perlmonks.org?node_id=348426

For a long time I used to write:

open my $fh,"<$file" or die "Reading $file:$!";

Lately ive started writing

open my $fh,"<",$file or die "< $!:$file";

I find using a 3 arg open is natural to me now, and I like the way the error message precedes the file. It seems to work better with long file names for one thing.

Anyway, what do you do?


---
demerphq

    First they ignore you, then they laugh at you, then they fight you, then you win.
    -- Gandhi


Replies are listed 'Best First'.
Re: Perl Style: About error messges opening files
by Abigail-II (Bishop) on Apr 27, 2004 at 10:56 UTC
    My style highly depends on the intended users, and how often it will be used. If it's a program I distribute, or write for someone else to use, I use something like:
    open my $fh => $file or die "Failed to open file '$file': $!\n";
    If it's code I just write for myself, I use something like:
    open my $fh => $file or die "open: $!\n";
    or
    open my $fh => $file or die "open '$file': $!\n";
    depending on how many files the program opens.

    If it's a 'write-today-discard-tomorrow' program, I use:

    open my $fh => $file or die "$!\n";
    or even
    open my $fh => $file or die;
    I use the latter style when writing on the command line too.

    Note that (except in the last case) I include a trailing newline. Most of the time, I couldn't care less on which line of the program an error occurs, especially not when it's about opening files.

    I've been writing some C lately, and I've started appreciating perror. Perhaps I should write a little module give similar functionality.

    Abigail

      I've been writing some C lately, and I've started appreciating perror. Perhaps I should write a little module give similar functionality.

      Hmm. A start would be to just tell those of us who have never heard of it a bit about it. :-) (Like me.)

      And I usually leave the die line numbering enabled in my scripts. Most of the time my scripts are running on cron jobs in the background and when files arent there its usually a serious matter and I want to be able to track it back from the line it died from. Since the error messages get logged automatically it doesnt much bother folks. When its user displayed ill often do something like

      die "File '$foo' doesnt seem to exist!\n" unless -e $foo; open my $foo_fh,"<"',$foo or die "<$!:$foo";

      So that the common case errors are trapped specifically and the general case stuff that probably indicate more serious things are trapped by the open.


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi


        A start would be to just tell those of us who have never heard of it a bit about it.
        PERROR(3) Library functions PERROR(3) NAME perror - print a system error message SYNOPSIS #include <stdio.h> void perror(const char *s); #include <errno.h> const char *sys_errlist[]; int sys_nerr; DESCRIPTION The routine perror() produces a message on the standard error output, describing the last error encountered during a call to a system or library function. First (if s is not NULL and *s is not NUL) the argument string s is printed, followed by a colon and a blank. Then the mes- sage and a new-line. To be of most use, the argument string should include the name of the function that incurred the error. The error number is taken from the external variable errno, which is set when errors occur but not cleared when non-erroneous calls are made. The global error list sys_errlist[] indexed by errno can be used to obtain the error message without the newline. The largest message number provided in the table is sys_nerr -1. Be careful when directly accessing this list because new error values may not have been added to sys_errlist[]. When a system call fails, it usually returns -1 and sets the variable errno to a value describing what went wrong. (These values can be found in <errno.h>.) Many library functions do likewise. The function perror() serves to translate this error code into human-readable form. Note that errno is undefined after a successful library call: this call may well change this variable, even though it succeeds, for example because it internally used some other library function that failed. Thus, if a failing call is not immediately followed by a call to perror, the value of errno should be saved. CONFORMING TO ANSI C, BSD 4.3, POSIX, X/OPEN SEE ALSO strerror(3) 2001-12-14 PERROR(3)

        And here's a way one might do it in Perl:

        package PError; use strict; use warnings; our ($VERSION) = q $Revision: 1.1 $ =~ /([\d.]+)/g; sub perror {warn @_ ? "@_: $!\n" : "$!\n"} sub pdie {die @_ ? "@_: $!\n" : "$!\n"} my %symbols = map {$_ => 1} qw /perror pdie/; sub import { my $class = shift; my $caller = caller; foreach my $name (@_ ? @_ : keys %symbols) { die "'$name' is not exported.\n" unless $symbols {$name}; my $func = $name eq "pwarn" ? "perror" : $name; no strict 'refs'; *{$caller . "::$name"} = \&{$class . "::$func"} } } 1; __END__
        when files arent there its usually a serious matter and I want to be able to track it back from the line it died from.
        I guess you and I differ. If a file is missing, than I will investigate why the file isn't there, and I'll start at the place that's supposed to produce the file. A line number in the program that figured out the file wasn't there doesn't do much for me.

        Abigail

      OT, but yet another style question for Abigail, since '=>' was like crazy again...

      I've noticed you use => in place of commas .. a lot. <seinfeld>not that there is anything wrong with that</seinfeld>. I'm still one that uses => only for hash construction, when I need to imply a key-value pair, or maybe in a map, but anyway, when I need the implicit LHS quoting and a comma.

      In one of your examples, you gave something like

      system mkdir => -p => $foo;

      while most would do something closer to:

      system("mkdir -p $foo");

      Care to explain why this is syllistically more interesting to you? My guess is no, but we're on a style thread and this just looks odd to me. I'm thinking you were nearly eaten by a pack of rabid commas when you were a child or something :) Ok, so I'm not going to use '=>' any more because of what you might say, I'm just curious ... sort of like a 'where did that accent come from' type of question.

      (commence with the moose stoning)

        The difference between system("mkdir -p $foo") and system mkdir => -p => $foo; is more than just the style of commas. Single argument system is subject to shell interpretation, while multi argument system is not.

        Having said that, I tend to use => to separate arguments which have totally different roles - most often to separate the first argument from the rest:

        push @array => 1, 2, 3, 4; printf "%s %6d %s" => $foo, $bar, $baz; pack "C" => $var; splice @array => 0, 3 => "foo", "bar", "baz";
        Etc. I've used that long before people started preaching that => was some kind of pair creator. It never was, and still isn't. And I'm not the only one using this style, Larry Rosler (of GRT fame) was a vivid user of this style as well.

        Abigail

        I would imagine that Abigail avoided:

        system("mkdir -p $foo");

        because that would be interpreted by the shell, and would foul up if (for example) $foo contained a filename with spaces. The advantage of using the arrow instead of the comma is that you can get away with less quoting, compare:

        system mkdir => -p => $foo; system 'mkdir', '-p', $foo;

        Same number of chars, arguably more readable.

        You surely mean the list variant of system: system "mkdir", "-p", $foo

      I never put a newline there, the line numbers at the error message do not cause any harm. (Line numbers as there are two: one for the source file and one for <>.)

      In one-liners I usually just use numbers after die:

      open my $F, "<", $name or die 1;
      I use numbers as they show where the error occured if there are multiple die statements in a one-liner, but they are short and easy to write. I add $! if the error really occurs and I don't know why it happened.
Re: Perl Style: About error messges opening files
by grinder (Bishop) on Apr 27, 2004 at 11:14 UTC

    I like having the error messages following the resource in question and a colon; all the rest of the Unix toolkit tends to follow that philosophy, and I don't see any value in bucking the trend.

    By force of habit I write my error messages out in longhand, in a vague attempt to make things clearer:

    open IN, '<', $file or die "Cannot open $file for input: $!\n"; open OUT, '>>', $file or die "Cannot open $file for append: $!\n"; open P, '|', $cmd or die "Cannot open input pipe from $cmd: $!\n";

    I'm in two minds about your idea. If a non-programmer were to read me the message (or a programmer without shell-redirection experience, which is pretty much the same thing) they might omit pronouncing < altogether or describe it uselessly (e.g. "arrow"). That's why I like wordy error messages.

    On the other hand, if I were to adopt this idea, I wouid be tempted to push things to extremes, and factor out the redirection into a variable:

    my $whence = '<'; open my $fh, $whence, $file or die "$whence $!:$file";

    ... on the principle that if you change it to <+ or something else you only have to do that in one place. But I'll be the first to say that that's getting silly.

    Colour me unconvinced. But I think the 3-arg open is the thing. For all of you who have tuned in recently, here's the canonical thread on the subject: Two-arg open() considered dangerous.

      On the other hand, if I were to adopt this idea, I wouid be tempted to push things to extremes, and factor out the redirection into a variable:

      Yep, I use a subroutine very much like that quite often. The sub usually looks more like:

      sub my_open { my ($file,$mode,$error_text)=@_; $mode||='<'; $error_text||="While opening in '$mode':$!:$file"; open my $fh, $mode, $file or Carp::confess $error_text; return $fh }

      I like having the error messages following the resource in question and a colon; all the rest of the Unix toolkit tends to follow that philosophy, and I don't see any value in bucking the trend.

      Good point.

      Colour me unconvinced.

      I wasnt trying to convince people, just to see what they thought. :-)


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi


Re: Perl Style: About error messges opening files
by Corion (Patriarch) on Apr 27, 2004 at 11:11 UTC

    I use the 3-argument form of open when I don't intend the script to be usable on 5.5, which happens more and more often.

    But I always make sure to quote the filename, because users give their files weird names that even can look like an error message. I always give the error message some more context, so I can later on grep the source program for the line where the error was raised:

    open F, "<", $file or die "Couldn't open '$file' : $!\n";

    Supposedly, there is only one place in the program where the message Couldn't open occurs of course. The idea of reversing the filename is interesting - that way, the interesting bits (filename and error message) are at the start and at the end of the "line" - using my way, the interesting bits clog together at the end.

    I prefer my style, of course :).

      I'll raise a glass to that! Through a bug in my code I recently scattered a bunch of fields all over a database I was working on where in qw(field_name value value value ...) the name was discarded, the first value became the name and everything else after was the new value. Quite ugly. It also explains why I had filenames which were SQL fragments.
Re: Perl Style: About error messges opening files
by Juerd (Abbot) on Apr 27, 2004 at 14:10 UTC
Re: Perl Style: About error messges opening files
by dragonchild (Archbishop) on Apr 27, 2004 at 12:09 UTC
    Although this isn't completely on point, I tend to put single-quotes around every variable in a print/warn/die statement. So, my version of your line would be:
    open my $fh,"<",$file or die "< $!:'$file'";

    That way, I can determine if it's zero, one, or two spaces in my filename. Or, if there are postpended spaces. (Once bitten, twice shy, I guess.)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

Re: Perl Style: About error messges opening files
by jonadab (Parson) on Apr 27, 2004 at 20:10 UTC

    I agree with Abigail that it definitely matters who's going to see this message. If it's going to show up in an error log, I just make sure it's a unique string that I can grep for easily, something like this:

    foo or die "sorrow, pestilence, Notepad: $!$/";

    As long as I don't use the same error string twice, it takes about two seconds to find the place where the problem is.

    If it's an error message that an end user is going to see, I do something rather different...

    open FOO, "<$foo" or fatalerror "Couldn't Read Foo: $!"; # and off in an include file somewhere I define... sub fatalerror { my $email = $ENV{SERVER_ADMIN}; use HTML::Entities; my $code = encode_entities(shift); my $fm = <<"FRIENDLYMESSAGE"; <p class=\"error\">Oops! I seem to have run into a problem! This is probably a bug in the software on this site, something that the webmaster needs to fix. If this problem persists, you can contact the webmaster at <a href="mailto:$email">$email</a> and report the problem. It might get fixed faster if you include the exact technical error code that follows. </p> <p class=\"error\">Technical Error Code: $code</p> FRIENDLYMESSAGE # And then I output a page containing $fm in the # usual fashion. }

    This is for CGI, which in my case is the only kind of interface used for stuff that end users see, but you can imagine a similar sort of thing for GUI stuff.


    ;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print

      I have a logging module that "plugs in" to perl. Anything (or nearly anyway) going out on STDERR or STDOUT, even via die or warn, is automatically captured and logged with the time it happened, the line of code responsible, what "channel" was used (ie STDOUT or STDERR) and a few other bits and bobs, some automatic and some provided by the author of the script using the module. It also send automatic success/failure emails including a full stack backtrace and running enviornment of the script (in the case of error anyway.) The result of all this is I havent had to grep a logfile for a specific message in a long time. When something I wrote or maintain crashes I get an email with all the relevent details. If I need to review the logfiles to see what else was happening at the time I have alot more to go on than the fact that I was sufficiently creative with my message such that it was unique in the script. Which I know from experience is not a given at all. :-)

      My point here is that if you are using proper logging then unique error messages and the like are unnecessary. If its going to be an end-user-facing error message of course this is different. But as I said elsewhere in this thread virtually all of my code runs as background tasks and pretty error messages arent called for.

      I actually have permission from my boss to release the module, but ive never had the time to clean it up and make it more portable. Its somewhat Win32 biased, with hooks for the event log and other win32 tricks, and since the equivelent Unixy bias annoys me (because I cant play with the toys) id rather not be responsible for the opposite.

      BTW, I assume your use of $/ is only for brevity... Ive been bitten once too often by that trick to think its particularly useful out of one liners.


      ---
      demerphq

        First they ignore you, then they laugh at you, then they fight you, then you win.
        -- Gandhi


      If it's going to show up in an error log, I just make sure it's a unique string that I can grep for easily

      Ehm. I always thought the filename and line number included in the error message (if you don't let it end in \n) made that unnecessary. They're not scary, not even to beginners. Beginners learn to ignore these things the minute they start using a unixish system, developers learn to look at the line mentioned. That way, you increase the chance of getting a patch with the bug report if it happens to be a bug.

      Besides, going to a certain line number is much faster and easier than grepping or searching.

      Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

        using a random uniq message allows you to easily change the message to something better later on.

        s/mumble foo:/missing input file:/ s/bonk:/unknown request:/ s/what the frell:/data base seems broken:/
        going to a certain line number is much faster and easier than grepping or searching

        Not in my experience. A line number is harder to hold in your head, so you probably have to copy-and-paste it from wherever (browser, email, log, ...) into the jump-to-line feature of your text editor. As opposed to just hitting ctrl-S and typing the first N characters of a phrase. By the time you get to the second word, you're there usually there.

        I always thought the filename and line number included in the error message

        If you're getting the error message from a logfile or at the command prompt maybe. If an end user sees the message and tries to remember it to tell you, the chances of a full filepath coming through accurately, much less a line number, are vanishingly close to zero. A phrase like "Twinkies and Spandex" is significantly more likely to get to you intact.


        ;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print