tags:

views:

412

answers:

10

I'm basing the majority on my Perl scripts on the following template/skeleton:

#!/usr/bin/perl -w

use strict;
use utf8;

$| = 1;
binmode(STDOUT, ":utf8");

# my code goes here.

The things achieved by this template:

  1. Enable warnings (-w)
  2. Enabling strict mode (use strict)
  3. Going pure UTF-8 (use utf8 + binmode(STDOUT, ":utf8"))
  4. Disable buffering ($| = 1)

My question is:

How can my template be improved to better reflect Perl best-practices?

+17  A: 

Replace the -w with use warnings. It allows you to disable warnings lexically should you need to. See perllexwarn.

The use utf8 pragma is for when your source code is in UTF-8. If it is, great. If not... I don't recommend adding things that you don't actually use. Similarly, don't set STDOUT to UTF-8 unless you're actually producing it.

Disabling buffering will reduce performance. Don't do it unless you need to, and then limit the scope to the block where it's necessary.

I like to include a statement to explicitly state the minimum version of Perl required to run the script. This makes the error messages more meaningful if it doesn't compile due to someone using an older version of Perl. e.g.

BEGIN { require 5.00801 }

I use that particular incantation instead of something like use v5.8.1 because it's backwards-compatible with the versions of Perl I'm trying to "support" with a meaningful error message.

Michael Carman
why not `use 5.00801`?
Evan Carroll
@EvanCarroll: `use 5.00801` is a syntax error under *really* old versions of Perl. (i.e. prior to 5.004) The `BEGIN { require 5.00801 }` syntax is valid and dies gracefully. Admittedly, it's a habit I got into when such versions were merely old and not positively ancient like they are today.
Michael Carman
There's nothing wrong with `use`. It's not ancient, and it dies in the same way that yours would, although it doesn't have the message "BEGIN failed--compilation aborted at ..."
brian d foy
@brian d foy: The `use VERSION` syntax was added in 5.004. Under older versions it dies with *syntax error at foo.pl line 1* while the BEGIN-wrapped require dies with *Perl 5.xx required--this is only version 5.yy*. I prefer the message that doesn't result in me getting reports of "your script is broken!" :D
Michael Carman
If they are using anything less than 5.004, they don't deserve support. Seriously.
brian d foy
+4  A: 

Not that it's something that can be achieved in a template as such, but you can enforce perl best practices by forcing developers to run perltidy and perlcritic on all source code. Furthermore perl best practices is a must read, if you ask me.

As for your template, the only thing you should consider changing are the parenthesis after the builtin binmode function, since they are not required (this is one of the many recommendations in the book).

klausbyskov
+7  A: 

How about throwing in some documentation?

=head1 NAME

name here

=head2 SYNOPSIS

short synopsis here
innaM
Oh! Someone thinks documentation isn't cool. Would be interesting to know why, though.
innaM
@innaM - not sure why whoever downvoted you thought so, but I personally prefer to have my *script* documented in a help, NOT via POD.
DVK
+1  A: 

Change the interpreter line to

#!/usr/bin/env perl

This prevents you from using -w, so you'll have to also

use warnings;
William Pursell
Good advice /if/ the script shouldn't require a distro-installed perl. Perl can be expected in /usr/bin/perl because unlike other languages it is in the FHS. If you don't care about running the perl that your distro shipped with use `/usr/bin/env perl` if you do, you can statically type the perl location.
Evan Carroll
But if you want to ensure that the behavior is the same whether the script is invoked directly or via 'perl script', you must use /usr/bin/env. Using /usr/bin/env makes the script more portable and gives the user more consistency.
William Pursell
For a specific case, consider what happens if /usr/bin/perl if 5.8, $HOME/bin/perl if 5.10, and PERL5OPT contains -Mfeature=5.10. In that environment, scripts that hard code /usr/bin/perl will fail, while scripts that use /usr/bin/env will work.
William Pursell
+10  A: 

Here's mine, although I must admit that sometimes I just start typing without using the template. I set it up as a modulino so it's easy to add testing to it later:

#!perl
package App::XXX;

use utf8;
use 5.010;

use strict;
use warnings;
use vars qw($VERSION);

$VERSION = '0.01_01';

__PACKAGE__->run( @ARGV ) unless caller;

sub run
 {
 my( $class, @args ) = @_;


 }

1;

If you want to set all of your filehandles to some encoding automatically, you could add the open pragma:

 use open IO => ':utf8';

I have another template for documentation that I add later.

Also, some people have editor sequences that they add as comments at the top or the bottom of the script. Perhaps:

# -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*-
# vim: ts=4 sts=4 sw=4:

Since I put my scripts into distributions, the installation process automatically fixes up the shebang line so it doesn't matter what I put there.

brian d foy
Thanks for your answer. I don't remember seeing anything before that mentioned explicit declaration of the source's encoding as a "best practice" but it totally makes sense. After reading your answer I ended up reading perlunitut, perlunifaq, and "The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)" (http://joelonsoftware.com/articles/Unicode.html).
molecules
Is there are reason you don't have the check on `caller()` before your call `run`? The stuff I've read on modulinos uniformly suggests using code like this: `__PACKAGE__->run( @ARGV ) unless caller();` to make the modulino `use`-able. I believe you even wrote some of those articles.
daotoad
@daotoad: That's what I get for editing code for the post. I've fixed that.
brian d foy
modulinos are great.
Paul Nathan
+2  A: 

Another thing you may want to look at is the module Toolkit on CPAN. Created by Damian Conway it allows you to assemble a set of often used modules and then use a single 'use' line instead of several. So instead of

use strict;
use warnings;
use foo;
use bar;

You'd have;

use Toolkit;

and all of the modules you placed in your toolkit would be available.

jeremiah
Toolkit uses source filters which are best avoided. ToolSet offers similar functionality without the danger of source filters. http://search.cpan.org/perldoc/ToolSet See `Why are source filters bad?` on SO for more info: http://stackoverflow.com/questions/1785852/why-are-perl-source-filters-bad-and-when-is-it-ok-to-use-them
daotoad
+1  A: 

I never use a shebang line in my .pl scripts, nor do I use use lib /path/to/special/lib, so as to allow these to be customized for each invocation:

perl -I/path/to/special/lib myscript.pl

/usr/local/perl5.10 -I/different/path/to/lib myscript.pl

And of course, every file starts with:

use strict;
use warnings;

Edit: I thought of a few more things, that I've started to use more recently:

# at least while the project is in initial development, so as to
# expose more places where the module might die; maybe turn off
# in production, depending on what other error handling is in place
use autodie;

# ...after all other 'use' lines at the top of a module: remove all
# unwanted imports cluttering up our namespace
use namespace::autoclean;
Ether
+1  A: 

Although not everything Perl I do is for the web, it happens often enough that I am compelled to add:

use CGI::Carp qw(fatalsToBrowser);

to my development template, which I comment out once it hits production.

MetaHyperBolic
+1  A: 

I'd say you want to be including a standard set of POD documentation in your template, with the normal set of headings that you see it most modules on CPAN. I like to include this at the end, but you might like it at the top or even interspersed through the code. e.g. at the end:

1;
__END__

=head1 NAME

My::Foo - The great new My::Foo!

=head1 VERSION

Version 0.01

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 METHODS 

=head2 new

=head1 AUTHOR

=head1 BUGS

=head1 ACKNOWLEDGEMENTS

=head1 COPYRIGHT & LICENSE

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

=cut

I find writing my module as if I was going to release it to CPAN, even if I never well is a good idea. For more wisdom and best practice check out module-starter. sudo cpan Module::Starter. This handy little tool will build you a complete module skeleton, which is most instructive to browse through. e.g.

$ module-starter --module=My::Foo --author=markp [email protected] --mb
$ tree My-Foo
Mark Pitchless
Thanks for the plug for Module::Starter. You can also look at RJBS's newer, fancier, and more complex Dist::Zilla.
Andy Lester
A: 

It doesn't make sense to me that you would want to, by default, turn off buffering.

Andy Lester
My scripts do generally not produce huge amounts of data, so the increased number of syscalls generated by disabling buffering are not an issue in the general case. When is disabling buffering for STDOUT a bad idea?
knorv
@knorv, basically any time that you are writing a CLI tool that is part of a pipeline not read by humans, e.g. cat big_giant_file.dat | my_perl_script.pl > processed.dat
charstar
Charles: I'm aware of that case, but assuming the amount of data written to STDOUT is small - are there any drawbacks that I'm missing?
knorv
Never add code to your program that isn't useful in some way. "It doesn't hurt" is not enough reason to keep code in your program, even if it's only one line.
Andy Lester