The Often Overlooked Perl Module Pod::Usage

Pod::Usage is one of those boring modules that doesn’t get the attention it deserves. Included in Perl core since 5.6.0, released fourteen years ago, it’s not a new kid on the block. It’s job isn’t sexy, so not lots of talks, blog posts, or tweets about it. There are only 5 ++s for the distribution on metacpan.org. It’s my goto module for all but the most trivial scripts, I probably use it second to only pragmas in scripts. (Haha, I made a funny.)

nn

Pod::Usage generates usage/help output for your scripts by extracting relevant bits from your POD. This means you write usage/help information once for the POD and you get both your man page and -h command line help. Let’s look at its usage by starting with the result. I’ll use otfile, a simple script for serving a file via HTTP from the command line for these examples.

nn

1n2n3n4n5n6n7n8n9n10n11n12n13n14n15n16n17n18n19n
$ otfile -hnUsage:n    $ otfile [-a -p 1234] nnOptions:n    --auto or -an            Auto port selection. Increments specified port until successful.nn    --port=N or -p Nn            Use specified port, defaults to 1234.nn    --multiple or -mn            Don't exit after serving the file the first time. To serve an            file to multiple people. Requires, CTL+C to exit.nn    --help or -hn            this help informationnn$

nnn

Nothing exceptional here, this is the style and content you would expect from any script on a POSIX like system. We get similarly unsurprising output for incorrect arguments.

nn

1n2n3n4n5n6n
$ otfile --invalid-arg-named-yaakovnUnknown option: invalid-arg-named-yaakovnUsage:n    $ otfile [-a -p 1234] nn$

nnn

So what does the source look like? One of Pod::Usage’s benefits is the simplicity in using it.

nn

1n2n3n4n
use Pod::Usage;nuse Getopt::Long;nnGetOptions( ..., 'help' => sub { pod2usage(1) } ) or pod2usage(2);n

nnn

That’s it in terms of code! You can substitute Getopt::Std or your other argument parser of choice just as easily. This was really cheating though since the real meat of it is in the POD. So anything special there?

nn

1n2n3n4n5n6n7n8n9n10n11n12n13n14n15n16n17n18n19n20n21n22n23n24n25n26n
=head1 SYNOPSISnn$ otfile [-a -p 1234] <file to serve>nn=head1 OPTIONSnn=over 8nn=item B<--auto> or B<-a>nnAuto port selection.  Increments specified port until successful.nn=item B<--port=N> or B<-p N>nnUse specified port, defaults to 1234.nn=item B<--multiple> or B<-m>nnDon't exit after serving the file the first time. To serve a file to multiplenpeople. Requires, CTL+C to exit.nn=item B<--help> or B<-h>nnthis help informationnn=backn

nnn

Nothing special in the POD. Pod::Usage with the source I used outputs either the POD’s SYNOPSIS section or the SYNOPSIS section and any section titled OPTIONS, ARGUMENTS or OPTIONS AND ARGUMENTS.

nn

That’s it! Now, head over to Pod-Usage on metacpan.org and give it some ++ love.

n

Logging Apple Battery Data with Perl

Inspired by a blog post and the code that goes with it at jradavenport/batlog, I created a perl script for logging Apple laptop battery details last August.

nn

My script logs 11 values using ~67 bytes per record. The oldest log I have begins 288 days ago and is 17,122,966 bytes for an average of 59.4 KB/day or 21 MB/year. The log is missing data points due to things like prolonged hibernation or sleep, theoretical max is around 35MB/year with 10 minute resolution.

nn

The repo is on GitHub.

nn

Sample Graphs

nn

A simple gnuplot script is included in the repo which produces the below graphs.

nn

A couple days of output:nSample Battery Graph

nn

9 months of output:nSample Battery Graph 9 Months

nn

Things get a bit squashed at 9 months but the most interesting value over long durations, battery health, is clearly visible.

nn

The Code

nn

Acquiring the data for this in Perl is exceedingly simple. Apple provides a binary called ioreg for interacting with the I/O Kit Registry. Asking for battery information with the arguments -rc AppleSmartBattery gets us almost JSON:

nn

1n2n3n4n5n6n7n8n9n
+-o AppleSmartBattery      {n      "TimeRemaining" = 36n      "AvgTimeToEmpty" = 65535n      "InstantTimeToEmpty" = 65535n      "ExternalChargeCapable" = Yesn      "FullPathUpdated" = 1401701615n      "CellVoltage" = (4193,4182,4193,0)n      ...

nnn

For our simple needs, massaging the data into proper JSON to use a parser is unnecessary. A regex can cleanly capture the names inside the quotes and their values. We then store the name value pairs in a hash.

nn

1n2n3n
if ( $line =~ m/"([^"]+)" = (.*)$/ ) {n    $data{$1} = $2;n}n

nnn

We create an array of the values we actually care about logging. This is done near the beginning of the script, before running and parsing the output of ioreg. If the amount of data was much larger, our simple parser could be made smarter to only store in the hash the values we care about. With only 39 values though, the slightly increased memory usage in the short lived cron job is preferable to the little bit of extra CPU to check if we want a value before storing it.

nn

1n2n3n4n
my @logged = ( qw(n        CurrentCapacity MaxCapacity Pcnt CycleCount LifePcntn        ExternalConnected IsCharging FullyCharged Temperature CellVoltagen) );n

nnn

We can then take advantage of hash slices to write out just the values we care about:

nn

1n
say $fh join( "t", time, @data{@logged} );n

nnn

@data{@logged} becomes a list of the hash values for the keys earlier specified in @logged.

nn

The full script weighs in at only 28 lines of straight forward code. See just it here.

nn

GitHub Repo

n