[oak perl] listing modules

Belden Lyman blyman at iii.com
Tue Sep 14 18:37:10 CDT 2004


On Tue, 2004-09-14 at 14:27, Steve Fink wrote:
> Belden Lyman wrote:
> > On Tue, 2004-09-14 at 12:54, Steve Fink wrote:
> >>Well, they all have to be findable through @INC, so would this get you 
> >>something close enough?
> >>
> >>  perl -le 'print foreach map { glob("$_/*.pm") } @INC'
> > 
> > 
> > No, that's not true. lib.pm, -I switch to perl, and installation of 
> > modules into home directories can all yield modules findable by a
> > particular instance of the perl interpreter, yet not located anywhere
> > within @INC.
> 
> Those are not installed modules. I certainly wouldn't want it to find 
> modules that I had downloaded but never installed, so I also wouldn't 
> want it to find modules in places like that.
> 

We're using different definitions of "installed". To me, any module
that is available on disk and *can be* loaded by a perl interpreter
is installed.

Ignoring modules in unusual locations simply because they're in
unusual locations seems...odd. It ignores a lot of possibilities: 
modules that accept plug-ins; virtual hosting customers, who often
must install modules under $HOME; etc.

Open-source perl programs sometimes install application-specific
modules into an application subdirectory. For example, I've got:

   ~blyman/bin/syndigator-0.12/lib/RSSReader/Feed/Message.pm

which is pretty nice when you think about it.

> > Even if that were the case, your one-liner wouldn't work for modules
> > such as IO::Socket::INET, because $_ would never be IO/Socket. A
> > different approach would be:
> > 
> >     perl -MFile::Find -le'find(sub{/\.pm$/ and print
> > $File::Find::name}, at INC)'
> 
> Good point! Yes, that is much better. It still isn't guaranteed to be 
> complete (it doesn't search the rarely-used .pmc files,

Hmm, I didn't know about .pmc files. It also wouldn't handle .pl
files loaded via 'do'.

> nor does it 
> handle CODE refs stuck into @INC by some installed module that then 
> allow you to find other modules)

File this trick under plug-in modules and see above :) (Actually, I'd 
forgotten about this.)

But I think you're helping me make my "perl modules are installed if 
they live on disk anywhere" argument... this does not convince me that
"perl modules are installed if they live under @INC".

> , and it will probably contain more than 
> you want (my @INC contains . by default, which I've never liked,

So write a module to disable it:

   package Acme::DotlessINC;

   my $i = 0;
   for ( @INC ) { /^\.$/ and last; $i++ }
   $INC[$i] = sub { undef };
   
   'potentially harmful';  # true enough
   __END__

Then either

   use Acme::DotlessINC;

in your scripts, or include it on the command line

   $ perl -MAcme::DotlessINC /path/to/your/script

> and 
> therefore finds all kinds of uninstalled modules depending on where I 
> run it from), but it seems about right.
> 

The only real reason I can think of to need to know all modules 
installed on a system is to be able to duplicate the setup on another
system. (Though the original poster probably has some different need
at hand.) Therefore finding all modules, including those in '.',
seems fine to me.

...I'm a little curious about all the uninstalled modules on your 
system, but you'd probably be curious about the 'cpan' group on my
machine :)

> The one problem I have with that script is that it is only giving me the 
> basename of each file, which makes it hard to figure out what A.pm is 
> (it's Net::FTP::A, or perhaps Net::DNS::RR::A).

Wierd; I get output such as

[blyman 3:41pm 5]$ perl -MFile::Find -le'find(sub{/\.pm$/ and print
$File::Find::name}, at INC)'
   /usr/lib/perl5/site_perl/5.8.0/Geo/Weather.pm
   /usr/lib/perl5/site_perl/5.8.0/Weather/Underground.pm
   /usr/lib/perl5/site_perl/5.8.0/Weather/Bug.pm

[blyman 3:42pm 6]$ perl -MFile::Find\ 999
File::Find version 999 required--this is only version 1.04.
BEGIN failed--compilation aborted.

What version of File::Find are you using?

>  I never use File::Find, 
> but from the docs I don't understand why not. At any rate, you can get 
> the full path with
> 
>   perl -MFile::Find -le 'find({wanted=>sub{/\.pm$/ and print 
> $File::Find::name}, no_chdir=>1}, at INC)'
> 

That does the same thing on my machine as the previous one-liner.

Curiouser and curiouser.

> but I'm sure there must be a better solution (especially one that gets 
> rid of the @INC entry from the beginning of the path!). Oh, and get rid 
> of the stupid current directory:
> 
>   perl -MFile::Find -le 'find({wanted=>sub{/\.pm$/ and print 
> "$File::Find::name $File::Find::dir"}, no_chdir=>1},grep{$_ ne "."}@INC)'
> 
> Are we recreating the script from perlmonks? I already deleted that 
> message, so I can't go back and check.
> 

Probably :)

> With my bias against File::Find (and for glob()), I'd probably do the 
> whole thing like
> 
>   perl -le '@d=map {[$_,$_]} sort {length($b)<=>length($a)} grep
> {!/^\./} @INC; while(@d) {($_,$d)=@{shift @d}; next if $e{$_}++; print 
> substr($_, 1+length($d)) if /\.pm$/; push @d, map {[$_,$d]} glob("$_/*") 
> if -d $_}'
> 
> but that took me a while to come up with, and has gone way over the 
> one-liner threshold. :-)

Gack, so it has.

If all you want to do is strip off the directories in @INC, then try

perl -MFile::Find -le 'find({wanted=>sub{/\.pm$/ and print \
$File::Find::name}, no_chdir=>1}, at INC)' | perl -pe 'BEGIN{$re\
=join"|", at INC}s,(?:$re)/,,'

Or in one fell swoop:

perl -MFile::Find -le'find({wanted=>sub{/\.pm$/ and push @f, 
$File::Find::name}, no_chdir=>1}, @INC); $re = join "|", @INC; 
for(@f){ s,(?:$re)/,,; print}'

Or, my favorite solution, which avoids File::Find altogether:

find `perl -le'print "@INC"'` -name '*.pm' | \
  perl -pe 'BEGIN{$re = join"|", at INC} s,(?:$re)/,,'

Though I'd just do

   find / -name '*.pm' | perl blahblahblah

because I really would want to know *all* .pm files on disk, not
just those under @INC.

>  I just prefer doing the search explicitly, 
> because I always find myself wanting more control and not wanting to try 
> to track down some oddly-named configuration setting to do half of what 
> I want.
> 

So long as all the files get found, I don't care whether the search
is depth-first or breadth-first. I don't mind digging through File::Find
to figure out how to use the module: it's a core module, so is worth
understanding. (Sometimes I need to write code for windows machines.)

ho hum
$0.001

Belden




More information about the Oakland mailing list