Gotchas with running a Perl script as a cron job

August 3rd, 2010    4 Comments

So you've written a Perl script and it runs fine from the command line using a command such as:

perl /home/alistair/scripts/myscript.pl

Great, so now you set up a cron job so that the script will run automatically. For example, let's say you want to run it every Monday morning at 4.20 am:

20 4 * * 1 perl /home/alistair/scripts/myscript.pl

 
However, when this runs it generates the following email:

Your "cron" job on myserver
perl /home/alistair/scripts/myscript.pl

produced the following output:

Can't locate DBI.pm in @INC (@INC contains: /usr/perl5/5.6.1/lib/sun4-solaris-64int /usr/perl5/5.6.1/lib /usr/perl5/site_perl/5.6.1/sun4-solaris-64int /usr/perl5/site_perl/5.6.1 /usr/perl5/site_perl /usr/perl5/vendor_perl/5.6.1/sun4-solaris-64int /usr/perl5/vendor_perl/5.6.1 /usr/perl5/vendor_perl .) at /home/alistair/scripts/myscript.pl line 15.
BEGIN failed--compilation aborted at /home/alistair/scripts/myscript.pl line 15.

 
What this means is that your script uses a Perl module (in this example, DBI.pm) which can't be found. The error message shows all the paths in the @INC list of paths where Perl looks for modules. So, for some reason, Perl found the module when you ran the script, but didn't find the module when cron ran the script as you.

The reason is that cron doesn't run in the same environment that you're using. When you connect to the server, you probably automatically change to your preferred shell (e.g. csh, ksh or bash) and load up a whole set of environment variables. This happens within the startup file – for example, .bashrc or .cshrc. But cron doesn't load up any of this, and it uses sh by default.

To see what's in @INC within your environment use this command:

perl -le 'print for grep {$_ ne q{.}and -d} @INC'

 
And you'll probably notice differences between the output you get and the contents of @INC listed in the error message.

So, to make sure @INC contains the necessary paths in any environment, load it up from within your Perl script. To do this, add something like the following to the top of your Perl script, just after the path to Perl:

BEGIN {
     push @INC,( '/usr/local/lib/perl5/5.8.2/sun4-solaris',
          '/usr/local/lib/perl5/site_perl/5.8.2/sun4-solaris',
          '/usr/local/lib/perl5/site_perl/5.8.0/sun4-solaris',
          '/usr/local/lib/perl5/site_perl/5.6.1/sun4-solaris' );
}

So now Perl will be able to find the missing Perl module. Sorted! Well maybe not.

If you now try running the script from the cron job you may get something like:

Your "cron" job on myserver
perl /home/alistair/scripts/myscript.pl

produced the following output:

ld.so.1: perl: fatal: relocation error: file /usr/local/lib/perl5/site_perl/5.8.0/sun4-solaris/auto/DBI/DBI.so: symbol Perl_PerlIO_stderr: referenced symbol not found Killed

Now it's complaining about not finding something called ld.so.1. The problem here is with your PATH.

On my server, ld.so.1 lives in /usr/lib. You should be able to find out where ld.so.1 is by running:

whereis ld.so.1

/usr/lib isn't exactly an obscure place to look, but it's not in the PATH when the cron job runs. So the easy solution is to explicitly set the contents of the PATH environment variable in the cron job:

20 4 * * 1 PATH=/usr/local/bin:/usr/bin:/usr/sbin:/usr/lib; perl /home/alistair/scripts/myscript.pl

Now, just before the Perl script is run, the PATH gets set to three paths: /usr/local/bin, /usr/bin, /usr/sbin and /usr/lib. The syntax used here is that required by the sh shell in which cron runs.

At last, job done. The script runs and you'll be emailed any output it generates.

Comments

  1. User Gravatar Bob said:

    September 1st, 2010 at 2:58 pm (#)

    This is an extremely helpful post. It's a relief to finally have an explanation for all the problems I've had with cron and Perl. But it confirms for me that two should never be used together. There are just far too many headaches. My Ubuntu system doesn't even produce the error you show at the top of the post, leaving the poor user completely in the dark regarding why the script isn't running. Pure rubbish.

  2. User Gravatar @Skootaman said:

    October 5th, 2011 at 2:57 pm (#)

    This has really helped me out today, many thanks!

  3. User Gravatar Anand said:

    December 8th, 2011 at 4:06 am (#)

    I have a perl script , when executed directly from command prompt. It works and displays the result. However, when I invoke the perl script from a shell script for scheduling, it doesnt work. My shell script is

    #a.sh

    at -f /tmp/a.pl now

    Even I am confused to find out where the error file in the system. I am completly in the dark right now. Any help would be appreciated.

    Thanks,
    Gulla

  4. User Gravatar Jan said:

    December 13th, 2011 at 6:01 am (#)

    Hi,

    I ran in to a similar problem. I used the -I ( as in India ) flag in perl to fix it.

    - i created a wrapper script wrapper.sh for my perl script
    - within the wrapper.sh, i added this call :
    /path/to/your/perl/bin -I/path/to/perl5lib/ -I/path/to/more/modules perlscript.pl

Leave a comment