How can I know where a Perl module is installed? - perl

I want to know where the IO::Socket::SSL module, or more specifically, where the file SSL.pm is located. I already know that I have installed IO::Socket::SSL because use IO::Socket::SSL works.

I always use something like this:
%> perl -MIO::Socket::SSL -e 'print $INC{"IO/Socket/SSL.pm"}';
and you get the path or an error if the module it is not installed in a proper path where perl can get it.
If you want to see if that module was installed:
%> perl -MIO::Socket::SSL -e 1
if you don't get any error, it's installed.
Sometimes it's important to see the version number of the installed package:
%> perl -MIO::Socket::SSL -e 'print $IO::Socket::SSL::VERSION';
Or, if you are working on Windows, you have to use double-quotes:
C:\> perl -MIO::Socket::SSL -e "print $IO::Socket::SSL::VERSION";

This should work
perldoc -l 'IO::Socket::SSL'
or alternatively in cmd.exe
perldoc -l "IO::Socket::SSL"
-l switch means "Display the module's file name". I find that it shows the fully qualified path to a module or (if applicable) to the module's external POD which is in the same directory as the module itself.

You can do:
perl -E'use IO::Socket::SSL; say $INC{"IO/Socket/SSL.pm"};'
But a rule of thumb it most modules are typically in /usr/share/perl5 on ubuntu.

The pmtools package provides an assortment of useful command line utils for finding where a package is installed (pmpath), what version it is at (pmvers), etc

Related

Running a Perl script from crontab when you use Perlbrew

I have tried the following and find it to work. This is done with a non-privileged user. First find out where your perl command is:
# which perl
Then check the value of PERL5LIB:
# echo $PERL5LIB
Then, at the crontab file of the user, do something like:
MAILTO=<my email address for the jobs output>
HOME=/home/myhome
PERL5LIB=/home/myhome/perl5/lib/perl5
0 2 * * * $HOME/<rest of path to perl>/perl $HOME/<path to my perl script> arg1 ...
This will run a job at 2am and seems to find all Perl libs correctly. My question is: is this complete and portable? Is there a better way?
I have seen a number of bash and perl scripts out there that are supposed to prepare the environment for the execution of a Perl script, but this seems to suffice. Any advice will be welcome!
EDIT: From the comments to the question, it seems that I am using a "bad" mixture of Perlbrew and local::lib. The way to make sure libraries get installed inside a particular Perlbrew version is answered here: How do I install CPAN modules while using perlbrew?. Both cpan and cpanm will install under PERL5LIB when you are using local::lib unless you explicitly tell them to do otherwise. Also cpanm seems to be better suited to working along with Perlbrew.
The shebang (#!) line of the script should point to the (perlbrew-installed) perl it is meant to run under. (This should be done as part of installing the script.) That's all you need.
0 2 * * * /path/to/script arg1 ...
If you already have multiple perl installations managed with perlbrew the easiest approach is to just use perlbrew exec to run your script. The -q and --with options allow you to silence superfluous output and select the specific version of perl to run the script/job. Try something like:
perlbrew exec perl -E 'say "Hello from $]\n"' (this will show errors from older versions (< 5.10) of perl that don't have the -E switch enabled by default).
perlbrew exec -q --with 5.26.1 perl -E 'say "Hello from $]\n"' (this will run the command and suppress informational output).
perlbrew exec -q --with 5.26.1 perl ~/script_from_heaven.pl (runs the script with the perl version requested).
perlbrew exec -q --with 5.26.1 ~/script_from_heaven.pl (runs the script with the perl version requested or hard-coded in the script's shebang line).
I tend to explicitly set PERL5LIB and use local::lib only when I need them or for certain users or environments where I exclusively install all CPAN modules in $HOME/perl5/lib/perl5 (a full application deployment, say). Otherwise I find running perl from perlbrew pretty convenient.
A couple of things I've found helpful: setting an alias for perlbrew environments that you want to keep stable for a particular use can be a useful way to manage multiple perls:
~/$ perlbrew alias create perl-5.24.0 stable-cronperl
~/$ perlbrew list
perl-5.8.9
perl-5.10.1
perl-5.24.0
cperl-cperl-5.26.1
stable-cronperl (5.24.0)
perl-5.26.1
NB: however the alias is only useful/useable as a stable #! shebang anchor for use at the top of your scripts if you want to make them executable:
#!/home/cronic/perl5/perlbrew/perls/stable-cronperl/bin/perl
You can't refer to an alias using --with for example:
perlbrew exec --with stable-cronperl ~/smart_comments.pl
Reporting this as either a documentation issue or a bug is on my to do list.

Perl script run via sudo couldn't find library, #INC contents changed

Perl script run via sudo couldn't find library. The path containing the required library doesn't exist in #INC when running via sudo.
sudo perl -e "print \"#INC\""
prints less paths than
perl -e "print \"#INC\""
I've tried change the sudoers file by
Defaults !env_reset
which doesn't help.
Is there a way to keep the #INC contents?
Environment variables to remove:
RUBYOPT
RUBYLIB
PYTHONUSERBASE
PYTHONINSPECT
PYTHONPATH
PYTHONHOME
TMPPREFIX
ZDOTDIR
READNULLCMD
NULLCMD
FPATH
PERL5DB
PERL5OPT
PERL5LIB
PERLLIB
PERLIO_DEBUG
JAVA_TOOL_OPTIONS
SHELLOPTS
GLOBIGNORE
PS4
BASH_ENV
ENV
TERMCAP
TERMPATH
TERMINFO_DIRS
TERMINFO
_RLD*
LD_*
PATH_LOCALE
NLSPATH
HOSTALIASES
RES_OPTIONS
LOCALDOMAIN
CDPATH
IFS
Environment variables to preserve:
XAUTHORIZATION
XAUTHORITY
TZ
PS2
PS1
PATH
LS_COLORS
KRB5CCNAME
HOSTNAME
DISPLAY
COLORS
Thanks for the help. The answer is:
Defaults !env_reset
Defaults env_delete-=PERLLIB
With the information you've provided we can really only make guesses. The environment variable that directly affects #INC in your Perl script is $PERL5LIB.
However the most likely cause is that $PATH is different in the root shell and you're actually running a different Perl binary, which will have different defaults for #INC compiled in. Use which perl and sudo which perl to test this theory.
To solve your problem, make sure the 'shebang' line (first line of your script) points to the Perl binary you want to use:
#!/usr/bin/perl
And then in your Perl script add the library paths you need, either explicitly:
use lib '/opt/path/to/site/lib';
or relative to the location of the directory the script itself is in:
use FindBin;
use lib "$FindBin::Bin/lib";
One solution is to have a wrapper script to set env:
to get the current env to write to a file, could move this file to a more appropriate location.
~/env_get.pl
sudo ~/env_wrapper.pl ~/test.pl
perl -e "print \"#INC\""
# this works for me, -p preserve the user ENV
sudo su -p - gliang -c 'perl -e "print \"#INC\""'
/usr/local/perl /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .
Details here:
https://unix.stackexchange.com/questions/202383/how-to-pass-environment-variable-to-sudo-su
I have a hard time to paste, here is the demo code
https://github.com/EverydayQA/prima/tree/master/perl_tools

Perl verbose output?

Is there are a way to get Perl debug output, similar to bash -x but in Perl?
I do not need strikt or diagnose messages (they compile the code but do not print the line that the Perl interpreter executes).
Assuming you are using some kind of unix you can use the Devel::Trace perl module.
If it is not installed you can install it from CPAN like this:
sudo perl -MCPAN -e 'install Devel::Trace'
Once you have it you can run your script like this:
perl -d:Trace myscript.pl
And it will do exactly what bash -x does (note that the name of the Trace package is case sensitive).

Why does Perl not want to require certain files when running under -T?

I recently noticed that on my system it is not possible to require 'lib/file.pl' when running under -T, but require './lib/file.pl' works.
$ perl -wT -e 'require "lib/file.pl";'
Can't locate lib/file.pl in #INC (#INC contains: /usr/lib/perl5/site_perl/5.14.2/x86_64-linux-thread-multi /usr/lib/perl5/site_perl/5.14.2 /usr/lib/perl5/vendor_perl/5.14.2/x86_64-linux-thread-multi /usr/lib/perl5/vendor_perl/5.14.2 /usr/lib/perl5/5.14.2/x86_64-linux-thread-multi /usr/lib/perl5/5.14.2 /usr/lib/perl5/site_perl/5.14.2/x86_64-linux-thread-multi /usr/lib/perl5/site_perl/5.14.2 /usr/lib/perl5/site_perl)
$ perl -wT -e 'require "lib/file.pl"'
Doing it without -T works in both ways:
$ perl -w -e 'require "lib/file.pl"'
$ perl -w -e 'require "./lib/file.pl"'
In taint mode, . is not part of #INC.
perl -w -e 'print "#INC"'
[..snip..] /usr/lib/perl5/site_perl/5.14.2 /usr/lib/perl5/site_perl .
perl -wT -e 'print "#INC"'
[..snip..] /usr/lib/perl5/site_perl/5.14.2 /usr/lib/perl5/site_perl
I could not find that behavior in the doc. Can someone please tell me where this is documented or why -T doesn't like . as a lib directory?
Erm... this is actually well documented, I suppose:
When the taint mode (-T ) is in effect, the "." directory is removed
from #INC , and the environment variables PERL5LIB and PERLLIB are
ignored by Perl. You can still adjust #INC from outside the program by
using the -I command line option as explained in perlrun.
... but that's only a half on an answer, I suppose. The reasons behind such decision are given here:
... the issue with #INC is really more of a problem with SUID scripts
than CGI scripts. When you have an SUID script that can execute with
the permissions of another user (such as root), Perl goes into
taintmode automatically.
For this SUID script case, it would be a huge security breach to have
the capability of loading libraries from the user's current directory.
If a script ends up having a bug where the library is not found in the
normal directory path, then a user could exploit this by writing their
own, malicious version of the library, putting it in the current
directory, and running the SUID script from their current directory.
However, this is not really the same problem with CGI scripts. User's
are not executing your script from arbitrary directories. Your web
server controls which directory the script is called from. So keeping
"." in #INC is not really a problem compared to SUID scripts which
operate under taint mode automatically.

Why won't a module installed by `cpanm` be recognized?

I installed perl-5.12.2 using perlbrew:
perlbrew install perl-5.12.2 -D=usethreads -D=useithreads -D=uselargefiles -f
I then switched to this version and installed IPC::System::Simple using cpanm.
However, when I try to run my script I get:
Can't locate IPC/System/Simple.pm in #INC (#INC contains: /home/dave/workspace/proj1/scripts/bin/../lib /home/dave/src/bioperl-live /home/dave/perl5/perlbrew/perls/perl-5.12.2/lib/site_perl/5.12.2/x86_64-linux-thread-multi /home/dave/perl5/perlbrew/perls/perl-5.12.2/lib/site_perl/5.12.2 /home/dave/perl5/perlbrew/perls/perl-5.12.2/lib/5.12.2/x86_64-linux-thread-multi /home/dave/perl5/perlbrew/perls/perl-5.12.2/lib/5.12.2 .) at /home/dave/workspace/proj1/scripts/bin/../lib/createLayout.pm line 14.
I also found the following dir:
~/perl5/lib/perl5/x86_64-linux-thread-multi/auto/IPC/System/Simple
but it's empty (I have no idea if this means something).
Try this step-by-step guide, paying close attention to steps 7 and 8 (and optionally 9).
What does which cpanm from the command line show? For you it should report:
/home/dave/perl5/perlbrew/bin/cpanm
If thats OK then what does ls -l /home/dave/perl5/perlbrew/bin/cpanm show? It should be pointing to:
cpanm -> /home/dave/perl5/perlbrew/perls/current/bin/cpanm
And finally ls -l /Users/barry/perl5/perlbrew/perls/current should be pointing to the Perl you've switched to in perlbrew:
/home/dave/perl5/perlbrew/perls/current -> perl-5.12.2
All three of these must be like this otherwise something is wrong.
If its not then one likely issue is that cpanm is pointing to another installed Perl. You need to have cpanm installed for each version of perl under perlbrew:
perlbrew switch perl-5.12.2
curl -L http://cpanmin.us | perl - App::cpanminus
Now if which cpanm still doesn't show the perlbrew path then you have a $PATH precedence issue in your .bash_profile (or equivalent) file. This can be fixed by making sure that your perlbrew line...
source /home/dave/perl5/perlbrew/etc/bashrc
... in the profile file is after any other export $PATH lines.
After re-login back in you can confirm that this is right by doing echo $PATH and you should see perlbrew at the beginning (the left) of the path string, ie. something like this:
/home/dave/perl5/perlbrew/bin:/home/dave/perl5/perlbrew/perls/current/bin:/usr/bin:/bin:/usr/local/bin: