2011 twenty-four merry days of Perl Feed

Stupid Command Line Tricks

the -M switch - 2011-12-05

The Basics of -M

perl understands a bunch of command line switches. Some of them, like -f, you will probably never use. Others, like -e and -d, you might use all the time. The -M switch is somewhere in between. You might use it in the most trivial case, but it can probably do a lot more than you know it can.

The general use case is this:

perl -MCarp::Always::Color some-script.pl

The general effect is that "use Carp::Always::Color;" is injected at the beginning of the program to be run, as if it had been in the source itself. It's great for things that affect the global execution environment, like Carp::Always, but it's got a bunch of other little uses.

Importing

If you want to write a one-liner that needs an import, for example, you can use the equals sign:

perl -MList::Util=first -E 'say first { /rjbs/ } @INC'

The string after the = is passed to List::Util's import routine. If it has commas, the string is split on commas and the result is passed in.

This is how local::lib's one-liner form works! local::lib has a tricky import routine, and you pass it a path name by using -M…=…

perl -Mlocal::lib=~/local/testing

By default, import is called with no arguments, because libraries given to the -M switch are loaded with use Module;. You can skip the imports by using -m instead. -mMODULE becomes use Module ();.

Version Checks

Usually, if I want to know what version(s) of a module I have installed, I run which_pm, but when I don't have it – or more likely, when giving advice to someone on IRC, I can use -M. If I think someone's seeing a bug because they've got an old version of Sub::Exporter, I can tell them to run:

~$ perl -MSub::Exporter\ 999
Sub::Exporter version 999 required--this is only version 0.982.
BEGIN failed--compilation aborted.

no MODULE

Most of the time, we load libraries with use, but no is useful, too. (Instead of calling import, it calls unimport.) Mostly, no is used by libraries that are loaded to forbid undesirable behavior. indirect lets you ban indirect method invocation. circular::require lets you ban circular module loading.

In your source for Foo.pm, you might write:

use strict;
use warnings;
no circular::require;

use Bar; # if Bar tries to "use Foo", we'll die because of no circular::require

...but circular::require affects the global behavior of require. You probably don't want to always load it everywhere. You just want to use it sometimes when testing. You can't say -Mcircular::require, because that would use use instead of no. To get no, just throw in another dash:

~$ perl -M-circular::require -MFoo -e0

The code above will be silent if Foo has no circular requirements, and emit a string of warnings of it has them.

Further Madness

So, for testing versions, I showed:

~$ perl -MString::Truncate\ 999

For importing, I showed:

~$ perl -MString::Truncate=trunc,elide -e '...'

What if we want to combine these? They're not really compatible. What if we want to pass something more complex than a list of strings? The bits after the equals sign aren't evaled, so they can't be complex.

The secret lies in understanding how that version-testing code worked. The space after "String::Truncate" and everything following that is just tacked into the template use MODULE REST ;

This means you can put almost anything there. Do you want to import a renamed version of trunc, but only if you have a recent version?

~$ perl -M'String::Truncate 1.100000 trunc => { -as => "truncate" }' -e ...

Your program will be run with the following line of code prepended:

use String::Truncate 1.1000 trunc =>{ -as => "truncate" };

This is where it gets a little nutty: perl doesn't care whether you give it more than one statement. You can put all kinds of stuff in -M's argument. In fact, if the stuff you want to put in is important, but the module isn't, you can just use something meaningless like 5:

~$ perl -M'5; warn "pid: $$\n"' your-actual-program.pl

The use 5 just ensures that you're under Perl 5 or later, and then the code you actually wanted to inject is added.

-M is a powerful tool for all kinds of code injection when you don't want to actually start editing the code you're running. Even better, once you've finished working with it, you can just delete your shell history file, and nobody will know the kind of horrible things you did with it.

See Also

Gravatar Image This article contributed by: Ricardo Signes <rjbs@cpan.org>