手で実行できる処理が、cronだと動かない場合

コンソールから手でたたいて実行できる処理を、cronに入れて定時に自動で実行しようとしても、正しく実行しない場合があります。大抵は、パスを認識していないことが多いです。

cronでエラーを出力する

crontabに引数を与えて、エラーをファイルに出力してみます。

20 12 * * * /work/prg/test.csh > /work/prg/test_1.log 2>&1

出力結果を見ると、

Can't locate Net/SMTPS.pm in @INC (@INC contains: /usr/local/lib64/perl5
 /usr/local/share/perl5/usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5
 /usr/share/perl5 .) at /work/prg/test.pl line 39. 
BEGIN failed--compilation aborted at /work/prg/test.pl line 39.

となりました。やはり、ライブラリが見つかりませんと怒られてしまっています。ファイルやプログラムのパスは、

$conv_prg = '/usr/bin/';
${conv_prg}convert

のように、あえてフルパスを設定し、実行すれば確実に動きます。でも、モジュールは

use Net::SMTPS;

と、パスを指定することができません。perl本体で、モジュールのパスを認識しているようです。

モジュールの保存場所の確認

では、どこにモジュールがあると認識しているのか、知りたいですよね。

使うのは、perldocコマンドです。

#perldoc -l Net::SMTPS
/root/perl5/lib/perl5/Net/SMTPS.pm

やはり、/rootに保存していました。ここにあると、cronで実行すると認識しないんですよね。他のモジュールはどこになるのかみてみますと、

# perldoc -l Net::SMTP
/usr/share/perl5/Net/SMTP.pm

と、/usrなので、cronで認識します。SMTPはもともと入っていたモジュール、SMTPSはCPANからインストールしたモジュールです。そこで、CPAN実行時のインストール先を見てみました。

Installing /root/perl5/lib/perl5/Net/SMTPS.pm
Installing /root/perl5/man/man3/Net::SMTPS.3pm

なんと、rootにインストールしてしまっていて、/usrなんちゃらに入っていませんでした。それはエラーが起きますよね。

どうやら、CPANの設定に何かがありそうです。

CPANの設定を変更する

ネットで調べてみた所、まとめている方がいらっしぃます。参考にさせて頂きました。

CPANの設定は、cpan[1]> o conf で見ることができます。

makepl_arg         [INSTALLDIRS=site]
mbuildpl_arg       [--installdirs site]

となっているのを確認します。[]が空欄でしたら、 vim ~/.cpan/CPAN/MyConfig.pm で記述します。

問題がなさそうでしたら、環境変数を見て見ます。

#printenv | grep perl 
PERL5LIB=/root/perl5/lib/perl5:/root/perl5/lib/perl5:
PERL_MB_OPT=--install_base /root/perl5
PATH=/root/perl5/bin:/root/.pyenv/plugins/pyenv-virtualenv/shims:/root/.pyenv/shims:/root/.pyenv/bin:/usr/lib64/qt-3.3/bin:/root/perl5/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
PWD=/root/perl5/lib/perl5/Net
PERL_LOCAL_LIB_ROOT=:/root/perl5:/root/perl5
PERL_MM_OPT=INSTALL_BASE=/root/perl5

ライブラリに/rootと指定されています。指定を解除したいですね。

環境変数にライブラリを指定しているファイルがあり、 ~/.bashrc になるとのことです。

ファイルを開くと、

export PERL_LOCAL_LIB_ROOT="$PERL_LOCAL_LIB_ROOT:/root/perl5";
export PERL_MB_OPT="--install_base /root/perl5";
export PERL_MM_OPT="INSTALL_BASE=/root/perl5";
export PERL5LIB="/root/perl5/lib/perl5:$PERL5LIB";
export PATH="/root/perl5/bin:$PATH";

これらをコメントアウトします。これで、次からのインストールは大丈夫になります。

すでにインストールしたモジュールは?

ただ、すでにインストールしたモジュールはどうしたらいいのでしょうか。

それは、@INCのパス( /usr/local/lib64/perl5とか )に、自分でモジュールを置くことがいいかなと。

でも、コピーはだめです。移動です!コピーだと、

# perldoc -l Net::SMTPS
/root/perl5/lib/perl5/Net/SMTPS.pm

と、何ら変わりません!コピーをしていても、元ファイルがあるとそちらの方が優先されるんですね。消すと何かあるといけないとそのままにしがちですが、ここは思い切りが大事です。移動をしましたら、

# perldoc -l Net::SMTPS
/usr/share/perl5/Net/SMTPS.pm

となりました。これで安心です。

それでもできない場合が…

crontabに入れて実行してみました。

ところが、エラーは吐き出さないのに、なぜか上手くいきません。もしやと思いますが、他のモジュールが影響していそうです。

モジュールのソースから、他のモジュールも確認

そこで、モジュールのソースを開いてみます。 コメント行に、

# Authen::SASL, MIME::Base64 should be installed.

と書いてありました。これは、気がつかないです。そのモジュールのパスも見て見ますと、

# perldoc -l Authen::SASL
/root/perl5/lib/perl5/Authen/SASL.pod

やっぱり…。一緒にインストールしたんでしょうかね。こちらも移動しておきました。そうしたら、やっとcronでも実行できました

まとめ

プログラムを書く際は、相対パスではなくフルパスを設定するようにしています。ただ、モジュールは使えるものと思って、気にしていませんでした。モジュールがどこにあるか、知っておくのが大切です。CPANとかで便利にインストールができるのですが、こちらの設定も見ておく必要がありますね。

unix

Posted by 管理者