特定の Perl モジュールがインストールされているか確認する方法

今 OS 上に 特定の perl モジュールがインストールされているか確認する代表的なやり方として、以下のようなものがあります。

  • cpan -D Module::Name でチェックする
  • perl -MModule::Name を実行する
  • find Module/Name.pm で探す
  • perldoc Module::Name でドキュメントが存在するか確認する

一応、やり方色々ありますが、それぞれ メリット・デメリットがあり、今一なところがあったりします。

なので、一つずつ確認しながら、最終的に一番最適なやり方は何か。 そして、複数のモジュールに対して インストール有無 を含めて、該当モジュールの 現在のバージョン情報 / アップデート可能な最新バージョン情報インストールパス を一覧化して一括で確認する方法についてご紹介します。

その1. cpan -D Module::Name でモジュール情報を確認する

シンプルにサマリ情報が分かりやすく表示されますが、情報取得の失敗・成功に関わらず、出力結果の表示スピードがやや遅い感があります。 (パパッと出てこない)

インストールされている場合
01
02
03
04
05
06
07
08
09
10
11
12
# cpan -D JSON::PP
Reading '/root/.cpan/Metadata'
  Database was generated on Wed, 25 Jul 2018 03:17:03 GMT
JSON::PP
-------------------------------------------------------------------------
        (no description)
        I/IS/ISHIGAKI/JSON-PP-2.97001.tar.gz
        /usr/lib64/perl5/JSON/PP.pm           #### パス
        Installed: 2.27203                    #### インストール済みのバージョン
        CPAN:      2.97001  Not up to date    #### 最新バージョンはこれ
        Kenichi Ishigaki (ISHIGAKI)
        ishigaki@cpan.org
インストールされてない場合
01
02
03
04
05
06
07
08
09
10
11
12
# cpan -D DBI
Reading '/root/.cpan/Metadata'
  Database was generated on Wed, 25 Jul 2018 03:17:03 GMT
DBI
-------------------------------------------------------------------------
Use of uninitialized value in concatenation (.) or string at /usr/share/perl5/App/Cpan.pm line 841.
        (no description)
        T/TI/TIMB/DBI-1.641.tar.gz
        Installed:                            #### 未インストール状態
        CPAN:      1.641  Not up to date      #### 最新バージョンはこれ
        Tim Bunce (TIMB)
        Tim.Bunce@pobox.com

その2. perl -MModule::Name を実行して確認する

  • 出力結果に何も出てなければ、モジュールがインストールされている
  • エラーが出てきたらインストールされてないことになる

詳細情報までは出てこないけど、入っているか・入ってないかに関しては、叩いた瞬間出てくるので確認しやすいです。

インストールされている場合
1
# perl -MJSON::PP -e ''
インストールされてない場合
1
2
3
# perl -MDBI -e ''
Can't locate DBI.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 .).
BEGIN failed--compilation aborted.

ただ、cpan でインストールしてないモジュールに関しては、検出されない場合もあります。 (開発ベンダーによって配布されたモジュールがインストール先にただ単にコピーされた場合 等)

この場合は、cpan -D Module::Name で確認すると出てくるけど、perl -MModule::Name -e '' するとエラーになります。

cpan -D vs perl -M
1
2
3
4
5
# cpan -D Socket6 | grep "Installed"
        Installed: 0.23

# perl -MSocket6 -e ''
perl: symbol lookup error: /usr/lib64/perl5/auto/Socket6/Socket6.so: undefined symbol: Perl_Gthr_key_ptr

その3. find Module/Name.pm で探す

その2. のように、パス登録されている階層から直接 .pm を find 検索すると cpan によってモジュールがインストールされてない場合でも見つけられるので、やり方としては有りだと思います。

ただ、現在のパスも含まれるので、余計な検索結果が出てきたり、場合によっては、find 特有の そのようなファイルやディレクトリはありません のような警告が出力されたりします。

find で Module/Name.pm を探す
1
2
3
4
5
6
# perl -e 'print "@INC"'
/usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .

# find `perl -e 'print "@INC"'` -name '*.pm' | grep "LWP/Protocol/https.pm"
/usr/lib64/perl5/LWP/Protocol/https.pm
./my_mod/lib/LWP/Protocol/https.pm

その4. perldoc Module::Name でドキュメントが存在するか確認する

ドキュメントが入ってるかどうか確認するだけなので、やり方もシンプルで 良く使われる確認方法ではないかと思います。

インストールされている場合 (ドキュメント出力)
1
2
3
4
5
6
7
8
9
# perldoc JSON::PP
NAME
   Socket6 - IPv6 related part of the C socket.h defines and structure
   manipulators

SYNOPSIS
   use Socket;
   use Socket6;
・・・

これだとドキュメントが全て出力されて長すぎるので、perldoc -l すると モジュール入っていれば、そのパスが出てきます。

インストールされている場合 (パス出力)
1
2
# perldoc -l Socket6
/usr/lib64/perl5/Socket6.pm
インストールされてない場合 (本当に?)
1
2
# perldoc -l DBI
No documentation found for "DBI".

一見良さそうに見えます。

しかし、実際にはインストールされていてもドキュメントが見つからなかったら perldoc は失敗します。 (落とし穴)

インストールされているけど、perldoc -l が失敗する例
1
2
3
4
5
# perldoc -l IO::Compress::Zlib::Constants
No documentation found for "IO::Compress::Zlib::Constants".

# cpan -D IO::Compress::Zlib::Constants | grep -i "install"
        Installed: 2.037

では、これはどうでしょう。

perldoc にモジュールファイルを出力するようにオプション指定するとドキュメント有り無しに関係なく、モジュールを見に行ってくれるので、単純にインストール有無だけを確認するならこれがシンプルで良いのではないかと。

インストールされいる場合
1
# perldoc -m IO::Compress::Zlib::Constants
インストールされてない場合
1
2
# perldoc -m DBI
No module found for "DBI".

よって、perldoc Module::Nameperldoc -l Module::Name よりは、perldoc -m Module::Name を実行してください。

これに加えて、もっと詳細が知りたい場合には、cpan -D Module::Name すると良いでしょう。

複数 モジュールのインストール有無、バージョン、及び パス情報を一括確認

これを踏まえて、以下のようにします。

  • モジュールが入っているかどうかの確認は、perldoc -m Module::Name
  • 詳細情報情報確認は、cpan -D Module::Name

ただ、確認したいモジュールが一つ二つだけなら単純に上記のコマンドを実行すれば良いですが、数が多くなると以下のようなスクリプトが必要になります。

MOD_LIST にモジュールリストをセットしてから実行してください。

  • -n : 見つからなかったモジュールのみ出力
  • -v : モジュールが見つからなくても、最新バージョン情報を出力
check_mod.sh
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/bin/bash

#### Flag Option 取得 [-n] [-v] のみ
#### -n : 見つからなかったモジュールのみ出力
#### -v : モジュールが見つからなくても、最新バージョン情報を出力
SHOW_NG_ONLY_FLAG=0;
SHOW_NG_MOD_NEW_VER_FLAG=0;

if [ $# -ne 0 ]; then
    for i in $@
    do
        [ "$i" = "-n" ] && SHOW_NG_ONLY_FLAG=1
        [ "$i" = "-v" ] && SHOW_NG_MOD_NEW_VER_FLAG=1
    done
fi

#### モジュールリストセット
MOD_LIST="JSON::PP Devel::StackTrace Class::Data::Inheritable \
Convert::ASN1 Crypt::OpenSSL::RSA Crypt::X509 Exception::Class UUID::Random \
Archive::Zip Compress::Zlib Compress::Raw::Zlib Path::Class Try::Tiny \
Crypt::SSLeay version IO::Compress::Base IO::Compress::Zlib::Constants \
Class::MethodMaker HTML::Parser UUID Data::Dump SOAP::Lite URI \
XML::SAX XML::NamespaceSupport XML::LibXML::Common XML::LibXML \
LWP LWP::Protocol::https Data::Dumper LWP \
MIME::Base64 Socket6 IO::Socket::INET6 Net::INET6Glue"

for i in $MOD_LIST
do
    MOD_PATH=''; CUR_VER=""; NEW_VER="";

    #### [ex] /usr/lib64/perl5/JSON/PP.pm
    perldoc -m $i > /dev/null 2>&1

    if [ $? -ne 0 ]; then

        #### -v 指定の場合 : モジュールが存在しない場合でも、最新バージョン情報を出力
        if [ $SHOW_NG_MOD_NEW_VER_FLAG -eq 1 ]; then
            NEW_VER=`cpan -D $i 2>&1 | grep -i "up to date" | tail -1 | tr -s " " | cut -d' ' -f2-` > /dev/null
        fi

        RES="[NG] $i        $NEW_VER"

    else

        [ $SHOW_NG_ONLY_FLAG -eq 1 ] && continue

        #### [ex] /usr/lib64/perl5/JSON/PP.pm
        #### [ex] Installed: 2.27203
        #### [ex] CPAN: 2.97001 Not up to date
        #### 普通は問題ないが、特定のモジュールで「Could not eval ・・・」を吐くことがあるので、
        #### エラーを全て標準出力してから、サマリだけ tail する。
        INFO=`cpan -D $i 2>&1 | egrep -i "(\.pm|install|up to date)" | tail -3 | tr -s " "` > /dev/null

        #### 結果を CSV 化して、cut すればもっと簡単に取れるけど、
        #### バージョンによって、cpan -D の表示順番が入れ替わる可能性がなくもないので、
        #### 少し効率悪いけど、1 行ずつ読み込みながら全てチェックする。
        while read line
        do
            echo $line | grep "^\/.*\.pm$"  > /dev/null 2>&1  &&  [ $? -eq 0 ] && MOD_PATH=$line
            echo $line | grep -i "^install" > /dev/null 2>&1  &&  [ $? -eq 0 ] && CUR_VER=`echo $line | cut -d' ' -f2`
            echo $line | grep -i "^cpan"    > /dev/null 2>&1  &&  [ $? -eq 0 ] && NEW_VER=`echo $line | cut -d' ' -f2-`
        done <<END
$INFO
END

        RES="[OK] $i        $CUR_VER (${NEW_VER})        $MOD_PATH"

    fi

    echo "$RES"

done
実行結果 (オプション指定 なし)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# ./check_mod.sh
[OK] JSON::PP        2.27203 (2.97001 Not up to date)        /usr/lib64/perl5/JSON/PP.pm
[OK] Devel::StackTrace        1.31 (2.03 Not up to date)        /usr/lib64/perl5/Devel/StackTrace.pm
[OK] Class::Data::Inheritable        0.08 (0.08 up to date)        /usr/lib64/perl5/Class/Data/Inheritable.pm
[OK] Convert::ASN1        0.26 (0.27 Not up to date)        /usr/lib64/perl5/Convert/ASN1.pm
[OK] Crypt::OpenSSL::RSA        0.28 (0.30 Not up to date)        /usr/lib64/perl5/Crypt/OpenSSL/RSA.pm
[OK] Crypt::X509        0.51 (0.51 up to date)        /usr/lib64/perl5/Crypt/X509.pm
[OK] Exception::Class        1.37 (1.44 Not up to date)        /usr/lib64/perl5/Exception/Class.pm
[OK] UUID::Random        0.04 (0.04 up to date)        /usr/lib64/perl5/UUID/Random.pm
[OK] Archive::Zip        1.28 (1.60 Not up to date)        /usr/lib64/perl5/Archive/Zip.pm
[OK] Compress::Zlib        2.037 (2.081 Not up to date)        /usr/lib64/perl5/Compress/Zlib.pm
[NG] Compress::Raw::Zlib
[NG] Path::Class
[NG] Try::Tiny
[OK] Crypt::SSLeay        0.55 (0.72 Not up to date)        /usr/lib64/perl5/Crypt/SSLeay.pm
[NG] version
[OK] IO::Compress::Base        2.037 (2.081 Not up to date)        /usr/lib64/perl5/IO/Compress/Base.pm
[OK] IO::Compress::Zlib::Constants        2.037 (2.081 Not up to date)        /usr/lib64/perl5/IO/Compress/Zlib/Constants.pm
[OK] Class::MethodMaker        2.10 (2.24 Not up to date)        /usr/lib64/perl5/Class/MethodMaker.pm
[OK] HTML::Parser        3.60 (3.72 Not up to date)        /usr/lib64/perl5/HTML/Parser.pm
[OK] UUID        0.03 (0.27 Not up to date)        /usr/lib64/perl5/UUID.pm
[OK] Data::Dump        1.15 (1.23 Not up to date)        /usr/lib64/perl5/Data/Dump.pm
[OK] SOAP::Lite        0.70 (1.27 Not up to date)        /usr/lib64/perl5/SOAP/Lite.pm
[OK] URI        1.37 (1.74 Not up to date)        /usr/lib64/perl5/URI.pm
[OK] XML::SAX        0.16 (1.00 Not up to date)        /usr/lib64/perl5/XML/SAX.pm
[OK] XML::NamespaceSupport        1.09 (1.12 Not up to date)        /usr/lib64/perl5/XML/NamespaceSupport.pm
[OK] XML::LibXML::Common        0.13 (2.0132 Not up to date)        /usr/lib64/perl5/XML/LibXML/Common.pm
[OK] XML::LibXML        1.63 (2.0132 Not up to date)        /usr/lib64/perl5/XML/LibXML.pm
[OK] LWP        5.805 (6.35 Not up to date)        /usr/lib64/perl5/LWP.pm
[OK] LWP::Protocol::https        undef (6.07 Not up to date)        /usr/lib64/perl5/LWP/Protocol/https.pm
[OK] Data::Dumper        2.145 (2.161 Not up to date)        /usr/lib64/perl5/vendor_perl/Data/Dumper.pm
[OK] LWP        6.35 (6.35 up to date)        /usr/local/share/perl5/LWP.pm
[OK] MIME::Base64        3.15 (3.15 up to date)        /usr/local/lib64/perl5/MIME/Base64.pm
[OK] Socket6        0.23 (0.28 Not up to date)        /usr/lib64/perl5/Socket6.pm
[OK] IO::Socket::INET6        2.71 (2.72 Not up to date)        /usr/lib64/perl5/IO/Socket/INET6.pm
[OK] Net::INET6Glue        0.6 (0.603 Not up to date)        /usr/lib64/perl5/Net/INET6Glue.pm
実行結果 (-n : NG のみ確認)
1
2
3
4
5
# ./check_mod.sh -n
[NG] Compress::Raw::Zlib
[NG] Path::Class
[NG] Try::Tiny
[NG] version
実行結果 (-n -v : NG のみ確認 & インストール可能なバージョンも一緒に出力)
1
2
3
4
5
# ./check_mod.sh -n -v
[NG] Compress::Raw::Zlib        2.081 Not up to date
[NG] Path::Class        0.37 Not up to date
[NG] Try::Tiny        0.30 Not up to date
[NG] version        0.9924 Not up to date

終わりに

オプションとして -n を指定すれば、cpan -D Module::Name でモジュール情報を取りに行かないので、入ってないモジュールだけパパッと確認できます。

-n -v のように、-v を加えると入ってないモジュールのリスト出力に加え、cpan -D Module::Name によって取得された現時点でインストール可能なバージョン情報も一緒に出力されます。

-v を付けたにも関わらず、バージョン情報が出てこなかったら不明なモジュール。 モジュール名を間違えた可能性があるので、ご参考までに。

特に、アプリによって数多くの要件モジュールと最低バージョンが決まってる場合には、現在の OS 上にモジュールが入っているかどうか、そして、そのモジュールがバージョンを満たしているかを一括確認する際に有効に使えます。

以上、CPAN 特定 Module のインストール有無、バージョン、パスを一括確認 でした。