perlbrewを使うにあたっていろいろな小細工をした件
最近perlbrewを使っています。で、いろいろ小細工をしたので問題点とその解決方法のまとめです。
補足すると、深遠な理由とかマリファナ海峡より深い事情とかがなければ、バッチ用、shebang用に(↓で書いてる)appperl的なラッパーを用意するだけでいけるんじゃないかと思います。
perlbrewedなperlの実行方法
perlbrewedなperlをどう実行するか、3つの局面にわけて考えます。
対話シェル上で
基本、~/.bashrcで $PERLBREW_ROOT/etc/bashrcを読めばOK。
なのですが、(深淵なる理由があり)同じUNIXユーザー(oreno)でプロジェクトごと(curryとgyoza)にperlbrewを使い分けたい
/home/oreno/curry/perlbrew/ /home/oreno/gyoza/perlbrew/
ので、こんなふうにしています。
# source this file in your ~/.bashrc PROJECT_HOME= if [ ! -z "$PROJECT" ]; then PROJECT_HOME="$HOME/$PROJECT" else PROJECT_HOME="$HOME" fi ### prepare perlbrew if [ -d "$PROJECT_HOME/perlbrew" ]; then PERLBREW_ROOT="$PROJECT_HOME/perlbrew" export PERLBREW_ROOT PERLBREW_HOME="$PROJECT_HOME/perlbrew" export PERLBREW_HOME [ -f "$PERLBREW_ROOT/etc/bashrc" ] && . "$PERLBREW_ROOT/etc/bashrc" [ -f "$PERLBREW_ROOT/etc/perlbrew-completion.bash" ] && . "$PERLBREW_ROOT/etc/perlbrew-completion.bash" fi
環境変数PROJECTはどうやってログイン時にセットされているかというと、~oreno/.ssh/authorized_keysで指定しています。
environment="PROJECT=curry" ssh-rsa ... environment="PROJECT=gyoza" ssh-rsa ...
curryとgyozaと担当者が同じ場合は、鍵を2つ作ってssh curryとssh gyozaとで鍵を使い分けるようないい感じの設定をSSHアクセス元の ~/.ssh/config に書けばいいと思います。
なお、environment= を使うには、sshd_config に PermitUserEnvironment yes が必要です。
ちなみに、sshで別ホスト(サーバーファーム内のwebサーバー達とか)にログインした際にもこの環境変数を引き継ぐようにするには、
sshd_configに、
AcceptEnv PROJECT
ssh_configに、
SendEnv PROJECT
と書けば実現できます。
PERLBREW_HOMEをPERLBREW_ROOTと同じに設定しているのは、空間を完全に分けたいからです。(PERLBREW_HOMEのデフォルトは~/.perlbrew)
スクリプトのshebang (#!)
$ perl ./mouyan.pl
とすればperlbrewedなperlで実行できるのですが、いちいち「perl 」と前置するのはめんどいです。
対話シェルでは、前掲の設定がなされていれば「perl」で実行されるのはperlbrewed perlなので、shebangを「#!/usr/bin/env perl」にすればOKです。
さて、shebangが「#!/usr/bin/perl」と書かれていて、かつ、書き換えられない|書き換えたくない深淵な理由がある場合はどうするかというと、/usr/bin/perlをラッパースクリプに置き換えちゃいます。
#!/bin/dash PERL=$(which perl 2>/dev/null) SYSTEM_PERL="/usr/bin/perl5.10.1" if [ -z "$PERL" ]; then exec $SYSTEM_PERL "$@" elif [ "$PERL" = "/usr/bin/perl" ]; then exec $SYSTEM_PERL "$@" else # maybe perlbrewed perl exec perl "$@" fi echo "failed to exec perl: $PERL" 1>&2 exit 2
「perl」でどのパスのperlが実行されるか調べて、perlbrewedっぽくなかったらsystem perlを実行するようにしています。
ここで注目して欲しいのでこのラッパースクリプトのshebangです。/bin/bashじゃなくて/bin/dashにしています。(dashはbashとは別のshの実装です)
bashは、環境変数 BASH_ENV が設定されていると非対話シェル(シェルスクリプトとか)でも BASH_ENV で指定したファイルが読まれます。
つまり、BASH_ENV=~/.bashrc な状態で、#!/bin/bashな/usr/bin/perl を実行すると、
- /usr/bin/perlはbashスクリプトなので、まず (BASH_ENVで指定された) ~/.bashrc が読まれる
- もし ~/.bashrc の中で perl を実行していると /usr/bin/perl が実行されて、
- /usr/bin/perlはbashスクリプトなので、まず (BASH_ENVで指定された) ~/.bashrc が読まれる
- ~/.bashrc の中で perl を実行しているので /usr/bin/perl が実行されて、
- /usr/bin/perlはbashスクリプトなので、まず (BASH_ENVで指定された) ~/.bashrc が読まれる
- ~/.bashrc の中で perl を実行しているので /usr/bin/perl が実行されて、
- \(^o^)/
となります。
なので、#!/bin/dash と書いています。
#!/bin/shでもOKです。/bin/shがbashのsymlinkでも、「sh」という名前で実行された場合はbashはその振る舞いを変え、BASH_ENVが評価されないからです。
cronで実行する場合
対話シェル上での話はこれで片付いたので、次は非対話な環境での実行時です。
対話シェルのときのようにはperlbrewの環境設定することができないので、~oreno/curry/bin/appperl や ~oreno/gyoza/bin/appperl にラッパースクリプトを使って、それ経由で実行するようにします。
#!/bin/bash # # ~oreno/curry/bin/appperl # export PERLBREW_ROOT=/home/oreno/curry/perlbrew export PERLBREW_HOME=/home/oreno/curry/perlbrew . $PERLBREW_ROOT/etc/bashrc if [ -z "$PERLBREW_PERL" ]; then perl_version=perl-5.16.1 else perl_version=$PERLBREW_PERL fi perlbrew use $perl_version exec perl "$@" echo "failed to exec: $@" 1>&2
最近のperlbrewですと、execでバージョン指定ができる
$ perlbrew exec --with 5.16.1 perl -v
のですが、
perl-5.16.1 ==========
というのが出力されるのを嫌って perlbrew use してから perl を実行するようにしています。
perlじゃなくてappperlなのは、このラッパースクリプト内で「perl」を実行しているので、PATH的に~oreno/curry/binが先にあると再度自分を実行して…とループしてしまうからです。(system perlを実行するようにすれば回避はできますが)
で、orenoユーザーのcrontabには、こんな感じに書きます。
### curry * * * * * ~/curry/bin/appperl ~/curry/batch/oomori.pl ### gyoza 0 8 * * * ~/gyoza/bin/appperl ~/gyoza/batch/3ninmae.pl 0 0 * * * ~/gyoza/analyze/gyoza-count.pl # ←これのshebangは「#!/home/oreno/gyoza/bin/appperl」
追記 2013-02-15
appperlを実行した場合、perlの起動が遅いです。なぜなら、
となるからです。
なので appperl はラッパースクリプトじゃなくて、perlbrewed perlへのsymlinkでいいんじゃないかと思っている今日この頃です。
もろもろの起動時間。
■ 1. bash $ time bash -c ':' real 0m0.028s user 0m0.005s sys 0m0.020s ■ 2. dash (bashとの比較参考用) $ time dash -c ':' real 0m0.004s user 0m0.001s sys 0m0.003s ■ 3. 厚いperlラッパー(appperl) (1+4+6 = 1+(1+5)+6) $ time appperl -e 1 real 0m0.465s user 0m0.319s sys 0m0.110s ■ 4. システムperlのラッパー (1+5) $ time /usr/bin/perl -e 1 real 0m0.011s user 0m0.002s sys 0m0.007s ■ 5. システムperlの実体 $ time /usr/bin/perl5.10.1 -e 1 real 0m0.007s user 0m0.002s sys 0m0.005s ■ 6. perlbrewed perlの実体 $ time /usr/oreno/perlbrew/perls/perl-5.16.1/bin/perl -e 1 real 0m0.008s user 0m0.000s sys 0m0.006s