This simple .pl script is supposed to grab all of the images in a directory and output an HTML — that when opened in a browser — displays all of the images in that dir at their natural dimensions.
From the mac command line, I want to just say perl myscript.pl and have it run.
… It used to run on apache in /cgi-bin.
#!/usr/bin/perl -wT
# myscript.pl
use strict;
use CGI;
use Image::Size;
my $q = new CGI;
my $imageDir = "./";
my #images;
opendir DIR, "$imageDir" or die "Can't open $imageDir $!";
#images = grep { /\.(?:png|gif|jpg)$/i } readdir DIR;
closedir DIR;
print $q->header("text/html"),
$q->start_html("Images in $imageDir"),
$q->p("Here are all the images in $imageDir");
foreach my $image (#images) {
my ($width, $height) = imgsize("$image");
print $q->p(
$q->a({-href=>$image},
$q->img({-src=>$image,
-width=>$width,
-height=>$height})
)
);
}
print $q->end_html;
Perl used to include the CGI module in the Standard Library, but it was removed in v5.22 (see The Long Death of CGI.pm). Lots of older code assumed that it would always be there, but now you have to install it yourself:
$ cpan CGI
Perl used to include the CGI module in the Standard Library, but it was removed in v5.22. Lots of older code assumed that it would always be there, but now you have to install it yourself.
The corelist program that comes with Perl is handy for checking these things:
$ corelist CGI
Data for 2020-03-07
CGI was first released with perl 5.004, deprecated (will be CPAN-only) in v5.19.7 and removed from v5.21.0
I handle this sort of thing by using the extract_modules program from my Module::Extract::Use module. Otherwise, I end up installing one module, then run again and discover another one to install, and so on:
$ extract_modules some_script.pl | xargs cpan
There's another interesting point for module writers. For a long time, we'd only list the external prerequisites in Makefile.PL. You should list even the internal ones now that Perl has a precedent for kicking modules out of the Standard Library. Along with that, specify a dependency for any module you actually use rather than relying it being in a particular distribution.
And, I was moving legacy programs around so much that I wrote a small tool, scriptdist to wrap the module infrastructure around single-file programs so I could install them as modules. The big win there is that cpan and similar tools install the prereqs for you. I haven't used it in a long time since I now just start programs as regular Perl distributions.
Related
I have made my own perl modules(pm files),named test.pm
package test;
use Exporter;
use strict;
use File::Basename qw(basename dirname);
use Cwd qw(abs_path);
use File::Path qw(make_path);
use FindBin qw($Bin $Script);
BEGIN {
our #ISA = qw(Exporter);
our #EXPORT = qw(mkdirOrDie);
our $VERSION = 1.0;
}
sub mkdirOrDie
{
my ($dir) = #_ ;
if(!-d $dir){
make_path($dir);
$dir=abs_path($dir);
# timeLog("Directory Created: $dir");
}
}
and I tried to install this module as follows,
h2xs -AX -n test
perl Makefile.PL
make
make install
there is no error,and I copy the test.pm to /usr/lib64/perl5/5.10.0/,but when i call sub function using test, an error has occurred,
Undefined subroutine &main::mkdirOrDie called at /to/my/path/main.pl line 92
is there something i ignored?
It's unclear at which point things started to go wrong for you.
Firstly, test.pm is a bad name for a Perl module. Perl modules should have names that begin with upper case letters (and Test.pm is already taken).
You should run h2xs before writing your code - as it generates a module skeleton for you fill in. I hope it hasn't overwritten your code with an almost empty file! It's also worth noting that most people stopped using h2xs many years ago. These days we have tools like Module::Starter.
Then, running, make install (which you need to do with root permissions - so usually with sudo) is what installs your module into the system libraries. There should be no need to run that cp command afterwards.
As for why your code doesn't find the module, there are many possible reasons. Are you using Perl 5.10 or do you have other Perl versions installed? What does the code look like that you are trying to use? Does test.pm still include the code you think it does?
Need more information to be much help here.
I need to run over 100 perl scripts (written by the former employee) on Windows for our system stability testing. Each script has several functions, and each function sends certain of linux commands to our back end system, and get results back. The result is written into a log file (currently each script has one log file). The results are “Success”, “Fail”.
Running these perl scripts one-by-one is killing my time. I am thinking about writing a batch file to automate it, but I have to parse the result files to generate test report. I searched online, and seems several testing frameworks, such as Test::Harness, Test::More, Test::Most are good choices. While based on my understanding, they only take .t file, and our scripts are normal perl scripts (.pl), and not standard perl test script (.t script). If using, say, Test::Harness, should I change all the perl script from .pl to .t, and put them under t folder? How to call my functions in Test::Harness? Can someone suggest a better way to automate the testing process and generate the test report like Test::Harness does? I think an example will be very helpful.
Test::Harness and friends isn't really an appropriate choice for this task, unless you want to modify all 100 of your scripts to emit TAP data instead of a log file.
Why not just write a Perl script to run all your Perl scripts?
use strict;
use warnings;
my $script_dir = "/path/to/dir/full/of/scripts";
opendir my $dh, $script_dir or die "Can't open dir $script_dir: $!";
my #scripts = grep { /\.pl$/ } readdir $dh;
foreach my $script( #scripts ) {
print "Running $script\n";
system 'perl', $script;
}
You could even parallelize this using fork and exec (or Parallel::ForkManager, even better), assuming that makes sense for your system.
One of us is confused here. These (100+) perl scripts aren't unit tests right?
If I'm correct keep reading.
Test::* you mentioned aren't really what you're looking for.
Sounds to me like you just need a main.pl, or a .bat, to run each test.pl.
So it seems you're on the right path. If it's possible to have all tests in the same directory, you can do something like this.
my $tests_directory = "/some/path/test_dir";
opendir my $dh, $tests_directory or die"$!";
my #tests = grep { $_ !~ /^\./{1,2}$/ } readdir $dh;
for my $test (#tests) {
system('perl', $test);
}
I have a Perl script that runs a different utility (called Radmind, for those interested) that has the capability to edit the filesystem. The Perl script monitors output from this process, so it would be running throughout this whole situation.
What would happen if the utility being run by the script tried to edit the script file itself, that is, replace it with a newer version? Does Perl load the script and any linked libraries at the start of its execution and then ignore the script file itself unless told specifically to mess with it? Or perhaps, would all hell break loose, and executions might or might not fail depending on how the new file differed from the one being run?
Or maybe something else entirely? Apologies if this belongs on SuperUser—seems like a gray area to me.
It's not quite as simple as pavel's answer states, because Perl doesn't actually have a clean division of "first you compile the source, then you run the compiled code"[1], but the basic point stands: Each source file is read from disk in its entirety before any code in that file is compiled or executed and any subsequent changes to the source file will have no effect on the running program unless you specifically instruct perl to re-load the file and execute the new version's code[2].
[1] BEGIN blocks will run code during compilation, while commands such as eval and require will compile additional code at run-time
[2] Most likely by using eval or do, since require and use check whether the file has been loaded already and ignore it if it has.
For a fun demonstration, consider
#! /usr/bin/perl
die "$0: where am I?\n" unless -e $0;
unlink $0 or die "$0: unlink $0: $!\n";
print "$0: deleted!\n";
for (1 .. 5) {
sleep 1;
print "$0: still running!\n";
}
Sample run:
$ ./prog.pl
./prog.pl: deleted!
./prog.pl: still running!
./prog.pl: still running!
./prog.pl: still running!
./prog.pl: still running!
./prog.pl: still running!
Your Perl script will be compiled first, then run; so changing your script while it runs won't change the running compiled code.
Consider this example:
#!/usr/bin/perl
use strict;
use warnings;
push #ARGV, $0;
$^I = '';
my $foo = 42;
my $bar = 56;
my %switch = (
foo => 'bar',
bar => 'foo',
);
while (<ARGV>) {
s/my \$(foo|bar)/my \$$switch{$1}/;
print;
}
print "\$foo: $foo, \$bar: $bar\n";
and watch the result when run multiple times.
The script file is read once into memory. You can edit the file from another utility after that -- or from the Perl script itself -- if you wish.
As the others said, the script is read into memory, compiled and run. GBacon shows that you can delete the file and it will run. This code below shows that you can change the file and do it and get the new behavior.
use strict;
use warnings;
use English qw<$PROGRAM_NAME>;
open my $ph, '>', $PROGRAM_NAME;
print $ph q[print "!!!!!!\n";];
close $ph;
do $PROGRAM_NAME;
... DON'T DO THIS!!!
Perl scripts are simple text files that are read into memory, compiled in memory, and the text file script is not read again. (Exceptions are modules that come into lexical scope after compilation and do and eval statements in some cases...)
There is a well known utility that exploits this behavior. Look at CPAN and its many versions which is probably in your /usr/bin directory. There is a CPAN version for each version of Perl on your system. CPAN will sense when a new version of CPAN itself is available, ask if you want to install it, and if you say "y" it will download the newer version and respawn itself right where you left off without loosing any data.
The logic of this is not hard to follow. Read /usr/bin/CPAN and then follow the individualized versions related to what $Config::Config{version} would generate on your system.
Cheers.
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.
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
}