How to see if a plugin is already installed with Perl - perl

I have a Perl script that requires a couple of plugins, for istance nmap. How can I see if the plugins are already installed and, in case they are not, install them? I tryed with the following code but it doesn't work very well, what I am trying to do is capture the "bash: nmap: command not found" output. I tryed with both stdout and stderr.
print "Checking nmap...\n";
my ($stdout, $stderr) = capture {
system("nmap");
};
if ($stdout=~m/command not found/) {
print "nmap not found, installing...\n";
system("rpm -i nmap-4.75-1.26.x86_64.rpm");
}
else {
print "nmap is already installed.\n";
}

How can I see if the plugins are already installed and, in case they are not, install them?
This is not a good idea, do not check for dependencies at run time. Instead you declare the dependencies in your distro meta file and check for them at build time and perhaps abort the build. The easiest way to do so is with requires_external_bin from Module::Install. This integrates nicely into the existing RPM infrastructure. - In other words, learn the basics of packaging and which problems this solves.
If you cannot rely on the user having permission to install it system-wide, create an Alien distro that downloads the source and installs it into the share tree. But once you go down that rabbit hole, be aware that it's deep - you would also need to take care of the deps of nmap itself somehow.

What about this?
my $cmd='which nmap';
my $output = `$cmd 2>&1`;
my $exit_value=$? >> 8;
if ($exit_value){
print "not found $cmd, error: $output\n";
}else{
print "Found $cmd at $output\n";
}

Like daxim pointed out requires_external_bin from Module::Install::External is a good way to ensure that your binary is installed.
If you can't use Module::Install in your application you might try searching the PATH environment variable like this:
use File::Spec;
$\="\n";
print installed($_) ? "$_ installed" : "$_ not installed" for qw/nmap ls cat nosuchfile less/;
sub installed {
my $name = shift;
foreach my $path (File::Spec->path()) {
my $bin = File::Spec->catfile($path, $name);
return $bin if -e -f -x $bin;
}
}
This should output something like this:
$ perl test.pl
nmap installed
ls installed
cat installed
nosuchfile not installed
less installed
The downside of this is of course, that the binary you are looking for has to reside in $PATH.

Related

Check the list of module installed in machine

I have made a script to intimate the admin the list of module need to be installed on machine .
I am trying to check wheather the module installed by underbelow code. The odd thing is that it is showing even installed module in the machine as not installed
#!/usr/bin/perl -w
my #module_list =('Smart::Comments','HTML::Parse');
foreach (#module_list) {
eval { require "$_" };
if (!($#)) {
print "Module Not installed : $_\n";
}
}
You need to use the string form of eval because require needs a bareword argument to match against the double-colon-separated form of the module name (e.g. Scalar::Util). (If it's not a bareword, then it needs to be a relative path, e.g. 'Scalar/Util.pm')
#!/usr/bin/perl
use strict;
use warnings;
my #module_list = ('Scalar::Util', 'flibble');
foreach (#module_list) {
if (!eval "require $_") {
print "Module not installed: $_\n";
}
}
There's my App::Module::Lister, which I designed as a modulino that can run as a module, a command-line utility, or a CGI script. It's a simple thing I needed for a friend who only had FTP access to a web server.
It gives you the list of everything in Perl's module search path, which I usually find easier than checking for specific modules each time. Once I have the whole list, I just look at the list.
You could check that you can load the module, but I tend to not like that because I don't want to potentially run the module's code to see if it's installed. It's usually not a problem, though.

How can I tell if a Perl module is core or part of the standard install?

How can I check if a Perl module is part of the core - i.e. it is part of the standard installation?
I'm looking for:
a command-line command:
a Perl subroutine/function to check within code
Perhaps the question should be: How can I tell what modules were originally provided with the specific Perl installation on a machine? (Actually, it is now asked as How can I tell what modules were originally provided with the specific Perl installation on a machine?.)
Given that there now appears to not to be an overall Perl standard installation, at least the answer to this new question will tell me what I originally had in the installation when it was first installed.
With that knowledge and if I keep the original installer image/package OR know how to get the exact thing again online, then I have a repeatable Perl installation for several machines with the knowledge of what modules will be present and what modules will not.
To clarify further: I am looking at what came with the installation originally, what modules were provided as part of that installation, and what was built-in. NOT what has been installed since then.
And I want to be able to do this on the machine that has the installation. So for this I would be relying upon the installation to have a record in some form as to what it has originally.
I asked spin-off question:
How can I tell what modules were originally provided with the specific Perl installation on a machine? (How can I tell what modules were originally provided with the specific Perl installation on a machine?)
The corelist command from the Module::CoreList module will determine if a module is Core or not.
> corelist Carp
Carp was first release with perl 5
> corelist XML::Twig
XML::Twig was not in CORE (or so I think)
Here is one way to use it in a script. The Module::CoreList POD is too terse -- you have to go hunting through the source code to find what methods to call:
use strict;
use warnings;
use Module::CoreList;
my $mod = 'Carp';
#my $mod = 'XML::Twig';
my #ms = Module::CoreList->find_modules(qr/^$mod$/);
if (#ms) {
print "$mod in core\n";
}
else {
print "$mod not in core\n";
}
__END__
Carp in core
You could check perlmodlib in a sub:
my %_stdmod;
sub is_standard_module {
my($module) = #_;
unless (keys %_stdmod) {
chomp(my $perlmodlib = `perldoc -l perlmodlib`);
die "cannot locate perlmodlib\n" unless $perlmodlib;
open my $fh, "<", $perlmodlib
or die "$0: open $perlmodlib: $!\n";
while (<$fh>) {
next unless /^=head\d\s+Pragmatic\s+Modules/ ..
/^=head\d\s+CPAN/;
if (/^=item\s+(\w+(::\w+)*)/) {
++$_stdmod{ lc $1 };
}
}
}
exists $_stdmod{ lc $module } ? $module : ();
}
Example usage:
die "Usage: $0 module..\n" unless #ARGV;
foreach my $mod (#ARGV) {
my $stdmod = is_standard_module $mod;
print "$0: $mod is ", ($stdmod ? "" : "not "), "standard\n";
}
Output:
$ ./isstdmod threads::shared AnyDBM_File CGI LWP::Simple
./isstdmod: threads::shared is standard
./isstdmod: AnyDBM_File is standard
./isstdmod: CGI is standard
./isstdmod: LWP::Simple is not standard
perldoc is most definitely part of the Perl's true core and standard installation. The source distribution for perl-5.10.1, for example, contains
perldoc.PL, generates perldoc as part of the standard installation
perlmodlib.PL, generates perlmodlib.pod as part of the standard installation
This is not a new addition. Perl-5.6.0, about ten years old, had perlmodlib as part of its true-core, standard installation.
Installations that do not contain these items are non-standard. Yes, I appreciate that it may seem academic from your perspective, but your vendor's packaging permitted a non-standard installation that breaks otherwise working programs.
With Debian's package manager, you can get the standard Perl installation with
$ apt-get --install-recommends install perl
There really is no such thing as "core" any more. There used to be a standard Perl distribution, but a lot of people don't have a standard Perl distribution. Operating system distributions modify it by either adding or removing modules, changing modules, and so on. You can't rely on the standard distribution being actually standard. Some Linux distributions don't even include the Perl documentation as part of the base Perl installation.
You mention that you can't use Module::CoreList because it isn't core, but if you can create files, you can install the module. You can even pretend that you wrote it yourself.
For the really lazy, there's the Core Modules list on the perldoc.perl.org website.
You can use (for example, search for Net::FTP):
perl -MNet::FTP -e 1
If it doesn't have output, then it's installed.
Other resources
perldoc perlmodlib
perldoc perllocal
A node from perlmonks
In a response to a comment of Gbacon's, you say that you want the answer to be platform neutral. I don't know of such a solution, but I wonder if it's even the right way to go.
If your goal is to find out on specific machines, I would use the tools that come with the platform. On Debian, that would include dpkg (pre-installed on any Debian system) or apt-file (not pre-installed necessarily) or other APT tools.
As an example, take a look at the output of this:
dpkg-query -L perl | less
You would obviously need to parse the output, but it strikes me as a start precisely because it is specific to the machine in question.
From the command-line:
Let's say that you want to know
whether module Tie::Hash is
installed.
To find out, execute
the following from the command line:
perl -MTie::Hash -e 1
If you don't get any output from the above command then the module is installed; if you get an error, it's not installed.
For making this check from within the script you can make use of Module::Load::Conditional.

How can I get the version and location of an installed Perl module?

Is there a smart way to detect whether a certain Perl module has been installed in your system?
My old sutpid way is to write a Perl script in which the only thing I do is just to use the module. If nothing croaks when I run the detect script, then I know the module has been installed, although I still don't know which version and where the module has been installed .
thanks in advance.
Something like:
perl -MModule -e 'print "$Module::VERSION\n"' 2>/dev/null || echo "Not installed"
would give you the version of a given module, or tell you it isn't installed. Usage would look like:
perl -MXML::Parser -e 'print "$XML::Parser::VERSION\n"' 2>/dev/null || echo "Not installed"
To find the module path, you could examine #INC to find possible locations, or you could perhaps look into perlwhich. There is also pmpath from pmtools.
The shortest thing I know of that doesn't involve a script or shell alias:
$ perl -MFoo::Bar\ 99
Foo::Bar version 99 required--this is only version 1.234.
(or the usual message about not being in #INC if it's not installed)
For the curious, this is the same as perl -e 'use Foo::Bar 99'.
instmodsh
NAME
instmodsh - A shell to examine installed modules
SYNOPSIS
instmodsh
DESCRIPTION
A little interface to ExtUtils::Installed to examine installed modules, validate your packlists and even create a tarball from an installed module.
SEE ALSO
ExtUtils::Installed
Here's a program which does that:
#!/usr/bin/perl
# testmod - test to see if a module is available
use strict;
use warnings;
my $mod = (shift #ARGV) || die "usage: $0 module\n";
# convert module-name to path
my $file = $mod;
$file =~ s{::}{/}gsmx;
$file .= '.pm';
# Pull in the module, if it exists
eval { require $file }
or die "can't find module $mod\n";
# Get the version from the module, if defined
my $ver;
{ no strict 'refs';
$ver = ${$mod . "::VERSION"} || 'UNKNOWN';
}
# And its location
my $from = $INC{$file};
print "module $mod is version $ver loaded from $from\n";
Use pmvers. Like the name suggests, it shows the version of an installed module. If a module is not installed, it fails with the familiar error message: Can't locate … in #INC (#INC contains: …)
Use pmpath from the same distribution to find a module's installation path.
I use these bash function/Perl oneliners to find the version number and location of Perl modules:
# equivalent to perldoc -l <module>
perlwhere() {
perl -wle'eval "require $ARGV[0]" or die; ($mod = $ARGV[0]) =~ s|::|/|g; print $INC{"${mod}.pm"}' $1
}
perlversion() {
perl -M$1 -wle'print $ARGV[0]->VERSION' $1
}
: [ether ~].2$; perlwhere Test::More
/usr/lib/perl5/5.8.8/Test/More.pm
: [ether ~].2$; perlversion Test::More
0.94
I don't know if there is any smart way for this. But what I usually
do is to make use of '-l' or '-m' option of perldoc. For example :
%perldoc -l XML::Simple
and the output is something like below,which is the full path of module file
.../lib/XML/Simple.pm
The advantage with this approach compared to yours is that, if the module is installed
the output contains the path for module location. However when the module is not
installed
or if it doesn't has a perldoc the error message shown is "No documentation found for ...",
making it impossible to distinguish if the error is due to missing module or missing
documentation. In such scenario the -m option becomes handy since it prints entire
contents of the file along with the path.
The pmvers utility and the other pmtools will do what you need. Otherwise here is a one-liner to find a module version:
perl -le 'eval "require $ARGV[0]" and print $ARGV[0]->VERSION' Some::Module
If you're looking for a cross-platform CLI (Linux, OSX, Windows), consider my whichpm utility; e.g.:
# Locate the Data::Dumper module, and also print
# version information and core-module status.
$ whichpm -v Data::Dumper
Data::Dumper 2.145 core>=5.005 /usr/lib/perl/5.18/Data/Dumper.pm
It can also find accidental duplicates and list all installed modules.
If you happen to have Node.js / io.js installed, you can install it from the npm registry:
[sudo] npm install whichpm -g
For manual installation instructions and more, see the repo; here's a direct download link to the latest version (will stay current).

How do I find all modules used in a Perl script and install them?

I have been given a few Perl scripts to deploy.
What is the easiest way to find and install all modules used by these scripts?
EDIT:
From what I can find there are no conditional includes or includes in evals.
Does my Module::Extract::Use help? There's an extract_modules program in the examples directory:
$ examples/extract_modules -l some_program
File::Spec
File::Spec::Functions
strict
warning
You can pipe that list to cpan.
$ examples/extract_modules -l some_program | xargs cpan
Once you have that list, which is only the first level of dependencies, you can make a script distribution that allows people to use the normal CPAN toolchain to install everything.
If there's something that doesn't work for you, modify the program to handle that. If you think it would be useful to other people, send a pull request. :)
I was hoping Module::ScanDeps which provides the command line utility scandeps.pl would be useful here but, to my dismay, Module::ScanDeps is apparently not intended for this particular purpose as scandeps.pl either ignores missing modules or (with -c or -x) croaks when the script uses a module that is not installed.
Here is a quick'n'dirty Perl script that tries to execute the script using do until it succeeds:
#!/usr/bin/perl
use strict;
use warnings;
use Term::Prompt;
my ($script) = #ARGV;
die "Provide script file name on the command line\n"
unless defined $script;
until ( do $script ) {
my $ex = $#;
if ( my ($file) = $ex =~ /^Can't locate (.+?) in/ ) {
my $module = $file;
$module =~ s/\.(\w+)$//;
$module = join('::', split '/', $module);
print "Attempting to install '$module' via cpan\n";
system(cpan => $module);
last unless prompt(y => 'Try Again?', '', 'n');
}
else {
die $ex;
}
}
If you do not want the script to be run, you can run perl -c $script, capture stderr output of that and parse for missing module messages and call cpan for each such module found until perl -c $script outputs "Syntax OK". That gives you a cleaner loop too. I'll look at this later.
You might miss dependencies loaded at run time using this technique.
Well this is the very simplistic way I solved this.
In a bash shell:
cat *.pl | grep "^use " | tr ';' ' ' | while read a b c; do echo $b; done | sort -iu > modules.txt
This gave me a file with only the module names, one on each line.
I then used this
cat modules.txt | while read a; do cpan $a; done
to invoke cpan to each module name in the file. And then sat there answering yes to CPAN's questions to install dependencies as appropriate.
Not pretty but this time it got the job done.
Or let PAR's pp do the work for you in collecting together everything you need in a single executable.

How can I determine CPAN dependencies before I deploy a Perl project?

Does anyone have any suggestions for a good approach to finding all the CPAN dependencies that might have arisen in a bespoke development project. As tends to be the case your local development environment rarely matches your live one and as you build more and more projects you tend to build up a local library of installed modules. These then lead to you not necessarily noticing that your latest project has a requirement on a non-core module. As there is generally a requirement to package the entire project up for deployment to another group (in our case our operations team), it is important to know what modules should be included in the package.
Does anyone have any insights into the problem.
Thanks
Peter
I've had this problem myself. Devel::Modlist (as suggested by this answer) takes a dynamic approach. It reports the modules that were actually loaded during a particular run of your script. This catches modules that are loaded by any means, but it may not catch conditional requirements. That is, if you have code like this:
if ($some_condition) { require Some::Module }
and $some_condition happens to be false, Devel::Modlist will not list Some::Module as a requirement.
I decided to use Module::ExtractUse instead. It does a static analysis, which means that it will always catch Some::Module in the above example. On the other hand, it can't do anything about code like:
my $module = "Other::Module";
eval "use $module;";
Of course, you could use both approaches and then combine the two lists.
Anyway, here's the solution I came up with:
#! /usr/bin/perl
#---------------------------------------------------------------------
# Copyright 2008 Christopher J. Madsen <perl at cjmweb.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either the
# GNU General Public License or the Artistic License for more details.
#
# Recursively collect dependencies of Perl scripts
#---------------------------------------------------------------------
use strict;
use warnings;
use File::Spec ();
use Module::CoreList ();
use Module::ExtractUse ();
my %need;
my $core = $Module::CoreList::version{'5.008'};
# These modules have lots of dependencies. I don't need to see them now.
my %noRecurse = map { $_ => 1 } qw(
Log::Log4perl
XML::Twig
);
foreach my $file (#ARGV) {
findDeps($file);
}
foreach my $module (sort keys %need) {
print " $module\n";
}
#---------------------------------------------------------------------
sub findDeps
{
my ($file) = #_;
my $p = Module::ExtractUse->new;
$p->extract_use($file);
foreach my $module ($p->array) {
next if exists $core->{$module};
next if $module =~ /^5[._\d]+/; # Ignore "use MIN-PERL-VERSION"
next if $module =~ /\$/; # Run-time specified module
if (++$need{$module} == 1 and not $noRecurse{$module}) {
my $path = findModule($module);
if ($path) { findDeps($path) }
else { warn "WARNING: Can't find $module\n" }
} # end if first use of $module
} # end foreach $module used
} # end findDeps
#---------------------------------------------------------------------
sub findModule
{
my ($module) = #_;
$module =~ s!::|\'!/!g;
$module .= '.pm';
foreach my $dir (#INC) {
my $path = File::Spec->catfile($dir, $module);
return $path if -f $path;
}
return;
} # end findModule
You'd run this like:
perl finddeps.pl scriptToCheck.pl otherScriptToCheck.pl
It prints a list of all non-core modules necessary to run the scripts listed. (Unless they do fancy tricks with module loading that prevent Module::ExtractUse from seeing them.)
You can use online web-service at deps.cpantesters.org that will provide you many useful dependency data. All modules on CPAN already have the link to the dependency site (on the right side of the module page).
In the past I have used Devel::Modlist which is reasonably good allowing you to go
perl -d:Modlist script.pl
To get a list of the required modules.
I have a Make-based build system for all my C/C++ applications (both PC-based and for various embedded projects), and while I love being able to do a top-level build on a fresh machine and verify all dependencies are in place (I check my toolchains in to revision control :D), I've been frustrated at not doing the same for interpreted languages that currently have no makefile in my build system.
I'm tempted to write a script that:
searches my revision control repository for files with the .pl or .pm extension
runs perl -d:Modlist on them (thanks Vagnerr!)
concatenating it to the list of required modules
and finally comparing it to the list of installed modules.
I'd then execute that script as part of my top-level build, so that anyone building anything will know if they have everything they need to run every perl script they got from revision control. If there is some perl script they never run and don't want to CPAN install what's required to run it, they'd have to remove the unwanted script from their harddrive, so the dependency checker can't find them. I know how to modify a perforce client to leave out certain subdirectories when you do a 'sync', I'll have to figure that out for subversion...
I'd suggest making the dependency checker a single script that searches for pl files, as opposed to an individual makefile to check dependencies for each script, or based on a hard-coded list of script names. If you choose a method that requires user action to have a script checked for dependencies, people will forget to perform that action, since they will be able to run the script even if they don't do the dependency check.
Like I said, I haven't implemented the above yet, but this question has prompted me to try to do so. I'll post back with my experience after I'm done.
The 'obvious' way - painful but moderately effective - is to install a brand new build of base Perl in some out of the way location (you aren't going to use this in production), and then try to install your module using this 'virgin' version of Perl. You will find all the missing dependencies. The first time, this could be painful. After the first time, you'll already have the majority of the dependencies covered, and it will be vastly less painful.
Consider running your own local repository of CPAN modules - so that you won't always have to download the code. Also consider how you clean up the out of date modules.
use Acme::Magic::Pony;
Seriously. It will auto-install Perl modules if they turn up missing. See the Acme::Magic::Pony page in CPAN.
Its a "horse that's bolted" answer but I've got into the habit of creating a Bundle file with all my dependencies. Thus when I go to a new environment I just copy it over and install it.
For eg. I have a Baz.pm
package Bundle::Baz;
$VERSION = '0.1';
1;
__END__
=head1 NAME
Bundle::Baz
=head1 SYNOPSIS
perl -MCPAN -e 'install Bundle::Baz'
=head1 CONTENTS
# Baz's modules
XML::Twig
XML::Writer
Perl6::Say
Moose
Put this in ~/.cpan/Bundle/ (or wherever your .cpan lives) and then install 'Bundle::Baz' like a normal CPAN module. This then installs all the modules listed under "=head1 CONTENTS".
Here is a quickie bash function (using the excellent ack):
# find-perl-module-use <directory> (lib/ by default)
function find-perl-module-use() {
dir=${1:-lib}
ack '^\s*use\s+.*;\s*$' $dir | awk '{ print $2 }' | sed 's/();\?$\|;$//' | sort | uniq
ack '^\s*use\s+base\s+.*;\s*$' $dir | awk '{ print $3 }' | sed 's/();\?$\|;$//' | sort | uniq
}