Why can't my Perl script load a module when run by cron? - perl

I have a bunch of Perl scripts that all run fine, yet need to have use Plibdata; up top.
I set up a cron job that runs (I get the confirmation email from root) and it spits back the following error message:
Can't locate Plibdata.pm in #INC (#INC contains: /install/lib /opt/perl58/lib/5.8.8/IA64.ARCHREV_0-thread-multi /opt/perl58/lib/5.8.8 /opt/perl58/lib/site_perl/5.8.8/IA64.ARCHREV_0-thread-multi /opt/perl58/lib/site_perl/5.8.8 /opt/perl58/lib/site_perl .) at ./x line 5.
BEGIN failed--compilation aborted at ./x line 5.
Line 5 is... you guessed it.... use Plibdata;
I am also attempting to set the environment as such:
use lib "$ENV{CARSPATH}/install/lib";
so maybe if I found the location of this plibdata, I could explicitly direct it that way?
My cron commands will be executed using /usr/bin/sh says crontabs...
Any suggestions?
This script works from the command line.

You don't say what Plibdata is. You also don't state if this works at your command prompt. I assume that it does.
Try this:
perl -MPlibdata -e 1
Assuming that doesn't spit the same error, try this:
perl -MPlibdata -le 'print $INC{"Plibdata.pm"}'
That will tell you where. (It's probably in your PERL5LIB env var if this works.) Then you can just add the appropriate "use lib" to the directory Plibdata.pm is in.
Also, be sure you're using the same perl in both locations - command line ("which perl") and in the cron job (try "BEGIN { print $^X }" at the top of your script).

Cron uses a different user env than your env when logged in. Are you able to run the script from the command line? If so, just set your env variables inside the cron above your current commands.

Clearly, Plibdata.pm is not installed in the default module paths on your system:
/install/lib /opt/perl58/lib/5.8.8/IA64.ARCHREV_0-thread-multi /opt/perl58/lib/5.8.8 /opt/perl58/lib/site_perl/5.8.8/IA64.ARCHREV_0-thread-multi /opt/perl58/lib/site_perl/5.8.8 /opt/perl58/lib/site_perl
You have three choices:
Install Plibdata.pm in a known Perl system path (site_perl is the classic option).
Make the PERL5LIB shell environment (or the equivalent command line -I option for Perl) include the installation path of the module.
Use use lib in your script. Remember that the use lib action is done at compile time, so your variable in the path may not be initialised. Try using the variable in a BEGIN block like this:
my $env;
BEGIN {
$env = $ENV{CARSPATH};
}
use lib "$env/install/lib";

Running your program from a wrapper script as others have suggested is probably my preferred method, but there may be a few other solutions:
If you're using a modern cron you may be able to do something like this in your crontab entry:
* * * * * CARSPATH=/opt/carsi x
replacing the asterisks with the appropriate schedule designators.
This will set CARSPATH for the x process and allow the use lib statement that passes the environment variable to work.
You can also, depending on your shell and cron implementation, store your environment setup in a file and do something like:
* * * * * source specialenv.sh && x
Where specialenv.sh contains lines like (for bash)
export CARSPATH=/opt/carsi
You may also be able to set environment variables directly in the crontab, should you choose to do so.

cron does not setup an environment for you when it runs your code, so the environment variable $CARSPATH does not exist. I suggest only running shell scripts from cron, setting the environment inside of the shell script and then running the program you really wanted to run.
example wrapper script:
#!/bin/bash
source ~username/.bash_profile
cd ~username
./script.pl
If you are using ksh or sh you may need to say
#!/bin/sh
. ~username/.profile
cd ~username
./script.pl
Remember to replace username with your username on the system. Also, if the script is not in your home directory you will want to execute it with the path to it, not ./.
You say source, or period space, to load a given shell script in the current shell. You load it in the current shell so that any environment settings stay with the current shell.
~/.bash_profile, ~/.bashrc, ~/.profile, /etc/profile, etc. are all files that commonly hold your environment setup. Which one you are using depends heavily on your OS and who set it up.

Although its not an 'answer', I resolved this issue by using DBI instead of the Plibdata.
Which is kind of milky because now I will have to change a couple scripts around... ahhhhh I wish there was something I could do to make the Plibdata work
I'm still going to try Chas. Owens answer to see if that works
didn't work for me... "interpreter "/bin/bash" not found"maybe it would work with people who have that interpreter
* * * * * CARSPATH=/opt/carsi ./x
works

Related

Perl backtick ignores everything past the first space

I have a command
my $output = `somecommand parm1 parm2`;
When I try to run this Perl script I get the message
Can't exec "somecommand" at .....
It seems it is not seeing anything past the first space in between the backticks. I have a friend who runs this in a different environment and it runs fine.
What could I have in my environment that would cause this? I am running Perl v5.20 but so is my friend.
Perl's not ignoring the command parameters, it's just mentioning only the part of the command that it has a problem with -- it can't find somecommand
Whatever your somecommand is, it's not a shell command and it's not in a directory listed in your PATH variable
Change PATH to add its location to the end and it will work for you. You can do that system-wide or you dan modify it temporarily in your Perl code by manipulating $ENV{PATH} before you run the command

Change shell within Perl script

I am looking for a nice way to get the following done:
So I have a script that I need to run in Python in Unix by calling from a Perl script that was, in turn, called from my Excel VBA macro in Windows using Plink. The Python script, due to dependency issues, has to run in either csh or bash, and I will need to use export/setenv to add a few libraries before running the script. However by default, perl runs in sh shell and as such, there is no way I can add in all the dependencies and have the Python script to run.
So, I am just wondering if there is EITHER: 1. a way for me to add dependencies to sh shell in the perl script, OR 2. force my perl script to run in csh (preferred, since for some reason .bashrc for the account runs into permission issues).
Thanks a lot!
How about "3. Set the appropriate environment variable in the Perl or Python scripts"?
$ENV{'PATH'} = ...
...
os.environ['PATH'] = os.pathsep.join(newpaths + os.environ['PATH'].split(os.pathsep))
(dunno how to get the path separator in Perl, sorz)
To force the shell to csh, try the following in Perl :
`/bin/csh -c "command_name"`;
Edit:
You can use ENV variable, like this. Try that :
$s = `/bin/bash -c 'VAR_FOO=753; echo \$VAR_FOO'`;
print $s;
I ended up just change the .cshrc script, apparently the addition to PATH, for some reason, did not work for me. After that, everything runs smoothly by putting all into one line
so basically it looks something like this
/path/to/.cshrc && /python/path/to/python
Hope that helps!

Running ksh script inside a perl code is not working

I have a Korn shell script at a location like /opt/apps/abc/folder/properties.env. I can execute it from Unix bash using the dot command:
. /opt/apps/abc/folder/properties.env
This works.
I have a Perl script abc.pl from which I am calling the script properties.env. I tried the following different:
system('/usr/bin/ksh','-c', '. /opt/apps/abc/folder/properties.env');
/usr/bin/ksh -c /opt/apps/abc/folder/properties.env;
system('. /opt/apps/abc/folder/properties.env');
None of the above work. I don't want to use exec because I want to return to the Perl script. What am I doing wrong?
The environment changes will only last as long as the life of the ksh session spawned by the system command. If you want the environment changes to affect the Perl script, then you have to source that file before you launch the Perl program.
If you need those environment variables in your perl code, (not in the environment where you called perl), you can also read and parse that properties.env and set the environment in the %ENV variable.
e.g
$ENV{'ENV_VAR1'}=VALUE_OF_ENV_VAR1
using system() spawn another process, as the other poster said. changing environment in the child does not affect the parent.

Perl script works but not via CRON

I have a perl script (which syncs delicious to wp) which:
runs via the shell but
does not run via cron (and i dont get an error)
The only thing I can think of is that it read the config file wrongly but... it is defined via the full path (i think).
I read my config file as:
my $config = Config::Simple->import_from('/home/12345/data/scripts/delicious/wpds.ini',
\my %config);
(I am hosted on mediatemple)
Does anybody have a clue?
update 1: HERE is the complete code: http://plugins.svn.wordpress.org/wordpress-23-compatible-wordpress-delicious-daily-synchronization-script/trunk/ (but I have added the path as above to the configuration file location as difference)
update 2: crossposted on https://forums.mediatemple.net/viewtopic.php?pid=31563#p31563
update 3: the full path did the trick, solved
The difference between a cron job and a job run from the shell is 'environment'. The primary difference is that your profile and the like are not run for a cron job, so any environment variables you have set in your normal shell environment are not set the same in the cron environment - no extensions to PATH, no environment variables identifying where Delicious and/or WP are hosted, etc.
Suggestion: create a cron job that simply reports the environment to a known file:
env > /home/27632/tmp/env.27632
Then see what is set in your own shell environment in comparison. Chances are, that will reveal the trouble.
Failing that, other environmental differences are that a cron job has no terminal, and has /dev/null for input and output - so interactive stuff does not work well.
it seems the problem is not in running perl, but locating the Config library
you should try:
perl -e "print #INC"
and run a similar perl script in cron, and read the output
it possible that they differ
I suggest looking at my answer to How to simulate the environment cron executes a script with?
This is an similar Jonathan's answer but goes a bit further.
Based on your crontab, and depending on your installation, the problem might be the "perl". As others note the environment, particularly the $PATH variable, is different for cron. perl may not be in the path so you need to put the full path to perl in the cron command.
You can determine the path with the command $ type perl
I run into the same problem ...
Perl script works but not via CRON => error: "perl: command not found"
... after an update from Plesk 12.0 to Plesk 12.5. But the existing answers were not very helpful for me.
It took some time, but than I found this thread in the Odin forum which helps me: https://talk.plesk.com/threads/scheduled-tasks-always-fail.331821/
They suggest the following:
/usr/local/psa/bin/server_pref -u -crontab-secure-shell ""
That deletes in the /var/spool/cron/crontabs files the line:
SHELL="/opt/psa/bin/chrootsh"
After that, my cron jobs run with out any error.
(Ubuntu 14.04 with Plesk 12.5)
If the perl script runs fine manually, but not from crontab, then
there is some environment path needed by the some package that is not
getting through `cron`. Run your command as follows:
suppose your cron entry like:
* 13 * * * /usr/bin/perl /home/username/public_html/cron.pl >/dev/null 2>&1
env - /home/username/public_html/cron.pl
The output will show you the missing package. export that package path in
$PATH variables

Why does my command-line not run from cron?

I have a perl script (part of the XMLTV family of "grabbers", specifically tv_grab_oztivo).
I can successfully run it like this:
/sw/bin/perl /path/to/tv_grab_oztivo --output /path/to/tv.xml
I use the full paths to everything to eliminate issues with the Working Directory. Permissions shouldn't be a problem.
So, if I run it from the Terminal (Mac OSX) it works just fine.
But when I set it to run via a cron job, nothing appears to happen at all. No output is created etc.
There isn't anything wrong with the crontab as far as I can see, because if I substitute a helloworld.pl for the actual script, it runs just fine at the right time.
So, what can I do to debug? I can see from looking at %ENV in the two cases that the environment is very different, but what other approaches can I take to debugging? How can I see the output of the cron job, which might be some kind of perl "die" message or "not found" message from the shell or whatever?
Or should I be trying to somehow give the cron version of the command the same environment as when it's running as me?
It's often because you don't get the full environment when running under cron. Best bet is to capture the ouput by using the command:
( /sw/bin/perl /path/to/tv_grab_oztivo ... ) >/tmp/qq 2>&1
and then have a look at /tmp/qq.
If it does turn out to be a missing environment, then you may need to put:
. ~/.profile
or something similar, into the execution chain of your cron job, such as:
( . ~/.profile ; /sw/bin/perl /path/to/tv_grab_oztivo ... ) >/tmp/qq 2>&1
If you're looking at %ENV in the two cases, I'd suggest that, as a first step in your perl script, set %ENV to what it is in a cron job, and then trying to run it from the command line. You may need to exec yourself once for this to take full control:
BEGIN {
if (exists $ENV{something_in_your_env_not_in_cron}) {
%ENV = (...);
exec $^X, $0, #ARGV;
}
}
Now try running it, and seeing if there's anything you can do to debug it (including running under perl -d if required). Most likely, you'll find that you end up adding items back into %ENV one at a time until it magically starts working (LD_LIBRARY_PATH is a good one for this, but ORACLE_HOME or DB2HOME for Oracle or DB2 apps might be good choices, too). Then you can either set the variable in your script, or in the crontab.
I'd run a simple shell script by absolute path from the cron command.
Inside that script, I'd ensure that I trapped stdout and stderr to a known (or knowable) file. I'd also ensure that enough of your environment is set. On Unix, you get almost no environment set at all when you run a command via cron - I'm not sure about MacOS X. The standard culprit for problems is PATH. I have a separate .cronfile that sets my working environment enough that I usually don't have problems - that's an analogue of .profile.
On occasion if you can't figure out what's going wrong with your command line, the simplest way to fix it is to turn the whole thing into a shell script. Ideally you shouldn't have to do this, but it can be the fastest way to solve the problem.
File: /files/cron1.sh
#!/bin/sh
/sw/bin/perl /path/to/tv_grab_oztivo --output /path/to/tv.xml
And then in cron:
/files/cron1.sh
This allows you to test the script independent of cron. Remember though that your login shell runs with different environment variables than cron does.
cron usually captures the output of stdout and stderr and e-mailes any output to the crontab owner.
Did you double check your crontab entry to make sure it's valid and will execute at the right time?
Make sure that the script does not need any environment variables set. Otherwise wrap it in another (bash) script, where you can set the environment variables that the other script expects.