cronにpythonのジョブを登録してvenv環境で周期実行するためには
#結論
使いたいvenv環境下のpythonを使えばよい。
$ crontab -e
* * * * * cd [絶対パス]; venv/bin/python foo.py
#具体例
一分に一回、日時をjsonline形式でファイルに書き出したい。
OS環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G87
ディレクトリを作り、venv仮想環境作り、今回使うパッケージjsonlinesをインストール。
$ mkdir [場所]
$ cd [場所]
$ python -V
Python 3.7.1
$ python -m venv venv
$ source venv/bin/activate
(venv) $ pip install jsonlines
pythonスクリプトを書く。処理された日時をjsonlineとして、out.jsonlに書き出す、というものです。確認のためprintも。
import datetime
import jsonlines
dt = datetime.datetime.now()
dict_now = {'date': str(dt.date()), 'time': str(dt.time())}
with jsonlines.open('out.jsonl', mode='a') as writer:
writer.write(dict_now)
print('updated: '+ str(dt))
処理させるとこんな感じでアウトプットが。
(venv) $ python dt2jsonl.py
updated: 2020-01-26 17:39:23.435616
(venv) $ cat out.jsonl
{"date": "2020-01-26", "time": "17:39:23.435616"}
python dt2jsonl.py
のままcronに登録してみる。 * * * * *
なので一分に一回処理されるはず。
エラーとprint結果を確認するため、>> /tmp/cron.log 2>&1
でstderrとstdoutを/tmp/cron.logに書き出すようにする。
(venv) $ crontab -e
* * * * * python dt2jsonl.py >> /tmp/cron.log 2>&1
待っていると/tmp/cron.log
にエラーが
(venv) $ tail -f /tmp/cron.log
python: can't open file 'dt2jsonl.py': [Errno 2] No such file or directory
dt2jsonl.py
が見つからないらしいので、cronがどこにいるのか確認。
(venv) $ crontab -e
* * * * * pwd >> /tmp/cron.log 2>&1
(venv) $ tail -f /tmp/cron.log
/Users/[ユーザ]
ユーザディレクトリにいるので、スクリプトの絶対パスを教えてあげる。
(venv) $ crontab -e
* * * * * python /Users/[ユーザ]/[場所]/dt2jsonl.py >> /tmp/cron.log 2>&1
(venv) $ tail -f /tmp/cron.log
Traceback (most recent call last):
File "/Users/[ユーザ]/[場所]/dt2jsonl.py", line 2, in <module>
import jsonlines
ImportError: No module named jsonlines
今度はpythonスクリプトにたどり着いたが、jsonlinesがないと。venv環境で実行されてないので、cronはどのpythonを使っているのか確認。
(venv) $ crontab -e
* * * * * which python >> /tmp/cron.log 2>&1; python -V >> /tmp/cron.log 2>&1
(venv) $ tail -f /tmp/cron.log
/usr/bin/python
Python 2.7.10
システムのpython2.7を使っているので、なんとかするべき。
どのpythonを使うべきかチェック。
(venv) $ which python
/Users/[ユーザ]/[場所]/venv/bin/python
venv下のpythonに書き換える。
(venv) $ crontab -e
* * * * * /Users/[ユーザ]/[場所]/venv/bin/python /Users/[ユーザ]/[場所]/dt2jsonl.py >> /tmp/cron.log 2>&1
ちょっと待つと、/tmp/cron.log
にprintの結果がでた。
(venv) $ tail -f /tmp/cron.log
updated: 2020-01-26 17:48:00.924120
(venv) $ cat out.jsonl
{"date": "2020-01-26", "time": "17:39:23.435616"}
out.jsonlにもjsonlineとして書き出されていると思いきや、最初に手動で実行したもので、cronで実行された結果ではない。
ユーザディレクトリで実行されたので、ユーザディレクトリにあった。
(venv) $ cat ~/out.jsonl
{"date": "2020-01-26", "time": "17:48:00.924120"}
{"date": "2020-01-26", "time": "17:49:01.102146"}
{"date": "2020-01-26", "time": "17:50:00.278025"}
アウトプットの場所が違いますが、cronはvenv環境で処理してくれた。
pythonスクリプトのアウトプットを絶対パスで指定するのもいいですが、
今回はcronの実行ディレクトリを変える。
(venv) $ crontab -e
* * * * * cd /Users/[ユーザ]/[場所]; venv/bin/python dt2jsonl.py >> /tmp/cron.log 2>&1
これで期待通りになった。
(venv) $ tail -f /tmp/cron.log
updated: 2020-01-26 17:55:00.837256
(venv) $ cat out.jsonl
{"date": "2020-01-26", "time": "17:39:23.435616"}
{"date": "2020-01-26", "time": "17:55:00.837256"}
ちょっと待つと
(venv) $ cat out.jsonl
{"date": "2020-01-26", "time": "17:39:23.435616"}
{"date": "2020-01-26", "time": "17:55:00.837256"}
{"date": "2020-01-26", "time": "17:56:00.986909"}
{"date": "2020-01-26", "time": "17:57:01.167183"}
{"date": "2020-01-26", "time": "17:58:00.273073"}