cronでOSの起動時にとあるユーザーとしてなにかを実行する際、 実行プログラムから見える環境変数はどうなっているのかを調べてみました。

実験1

環境変数を調べるテスト用スクリプトとして次のようなものを用意しました。

#!/bin/bash
data >> result
echo $HOME > result
echo $PATH > result

HOMEは実行したユーザーのものに、 PATHはユーザーの.bash_profileの内容などが反映されていることを期待しています。 .bash_profile内でpyenvのためにPATHの設定などが書かれているので、 PATHの内容を見ることで.bash_profileの内容が反映されているのかを見ていきたいと思います。

ちなみにcronの設定ファイル内で環境変数を設定したりすることもできるのですが、 普段のターミナル上での設定をなるべく手を加えずにcronで実行するスクリプトなどにも反映させたいという思いがあるので.bash_profileなどが反映されるかどうかを調べます。

cronを使ってOS起動時にユーザー(idt12312)からスクリプト(tesh)を実行するために まずは次のコマンドを実行します。

$crontab -u idt12312 -e

idt12312というユーザー固有のcronの設定ファイルが開くので、

@reboot /home/idt12312/test

を追記して保存します。 再起動するとtestがちゃんと実行されたので、/home/idt12312/resultが作成されました。 その中身はこんな感じです。

Fri May 20 10:30:56 JST 2016
/home/idt12312
/usr/bin:/bin

HOMEは意図した通りに実行したユーザーのものになっていましたが、 PATHには.bash_profileの内容などが反映されていませんでした。

次にtestを次のように変更します。

#!/bin/bash -l
data >> result
echo $HOME > result
echo $PATH > result

変更点は一行目に-lが追加されたことです。 -lを追加することで、bashはログイン時と同じように動くらしいです。 つまり.bash_profileなどを読み込んでくれるらしいです。

再起動後にresultの内容を見るとこうなっていました。

Fri May 20 10:35:07 JST 2016
/home/idt12312
/home/idt12312/.pyenv/shims:/home/idt12312/.pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games

PATHに.pyenvが含まれているので、ちゃんと.bash_profileが読み込まれたみたいです。

実験2

次にpythonのスクリプトを起動時に実行する場合について考えます。 実験1のような環境変数がどうなるかに加えて、 ユーザのホームフォルダにあるpyenvをうまく使えるかも見ていきたいと思います。

テスト用のこんなpythonのスクリプトを用意します。

# -*- coding: utf-8 -*-
import os
import sys

python_version = sys.version_info.major
env_home = os.environ.get('HOME')
env_path = os.environ.get('PATH')
result = "PYTHON:{0}\nHOME:{1}\nPATH:{2}\n".format(python_version, env_home, env_path)

with open('result_py','a') as f:
    f.write(result)

システムのpythonのバージョンは2、 pyenvでglobalに設定したpythonのバージョンは3なので resultのPYTHONのところを見ることでシステム側のpythonが使われたのか pyenv側のpythonが使われたのかを判断することができます。 pyenvの初期化などは.bash_profileに書かれているので、.bash_profileさえ読み込まれたらpyenvも使えるようになるはずです。

検証をするために、まずはcronに次のような設定をしてみます。

@reboot python /home/idt12312/test.py

するとresultの内容はこうなりました。

PYTHON:2
HOME:/home/idt12312
PATH:/usr/bin:/bin

PATHとHOMEは実験1の時と同じであり、pythonはシステムのものが使われたみたいです。

次にcronに次の設定をしました。 一旦bash -lをかませることで、.bash_profileの内容を環境に反映させようという作戦です。

@reboot bash -l python /home/idt12312/test.py

するとresultの内容はこうなりました。

PYTHON:3
HOME:/home/idt12312
PATH:/home/idt12312/.pyenv/versions/3.5.1/bin:/home/idt12312/.pyenv/libexec:/home/idt12312/.pyenv/plugins/python-build/bin:/home/idt12312/.pyenv/plugins/pyenv-virtualenv/bin:/home/idt12312/.pyenv/shims:/home/idt12312/.pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games

PATHに.bash_profileの内容が反映されるだけではなく、ちゃんとpyenvのpythonが呼び出されているみたいです。

cronが実行するコマンドから考えると、まずはbash -lで.bash_profileが読み込まれて pyenvが初期化されます。 pythonを呼び出す段階ではpyenvが初期化されているので、 pyenvのpythonが呼ばれるという仕組みです。

まとめ

bashをログインしていない状態で使う場合でも-lオプションをつけてやることで ログインシェルとして動かしている場合と環境変数を同じにできるのがポイントみたいです。