【python】pythonでcrontabを編集してプログラムの自動実行を試みる。

crontab

ヤマモトです。

先日、件のジャンプ感想記事自動化について、今後のタスクを整理した。

件(くだん)の件

今回はそのうち、「ジャンプの発売日になったらプログラムを実行する」を処理するために、pythonでcrontabを実行できないかを試す話。

この時はまだ誤りに気付いていません。最後に何がイマイチだったのかを話しますが、みなさんも何がイマイチなのかを考えて読み進めてみてください。

crontabについて

crontabの概要

wikipediaがわかりやすいので、知らない人はこちらを見てください。

Unix系オペレーティングシステム (OS) において、コマンドの定時実行のスケジュール管理を行うために用いられるコマンドである。標準入力からコマンド列を読み取り、crontabと呼ばれるファイルにそれを記録する。この記録を元に定時になると、その命令内容を読み取り、実行が行われる。

wikipedia: crontabより引用

crontabの実行と記法

使い方自体はいたってシンプル。記法はそこまで難しくは無いが、細かい指定はググった方が良い。

# 現在設定されているcrontabの表示
crontab -l

# crontabの編集
crontab -e

# 記法
# (行頭の # マークはコメント行を示す)
# +------------ 分 (0 - 59)
# | +---------- 時 (0 - 23)
# | | +-------- 日 (1 - 31)
# | | | +------ 月 (1 - 12)
# | | | | +---- 曜日 (0 - 6) (日曜日=0)
# | | | | |
# * * * * * 実行されるコマンド

# 例
10 * * * * echo "Hello" > /home/hogehoge/hello.txt  # 毎時10分になったら"Heloo"をhello.txtに追記する。

ちなみに、crontabはユーザーごとに管理され、特に指定しなければ自分自身を実行者とするcrontabが作成される。ユーザーの指定は -u で行う。

crontabの保存場所

今回の肝。

crontabはユーザーごとに編集され保存されるわけだが、その保存場所を今回は知りたい。
じゃないと、pythonで編集できない…

調べてみると以下記事が見つかった。

/var/spool/cron にユーザー名をファイル名として保存されているようだ。

実際に確認してみた。

実際にヤマモトが使っているLinux (fedora) 環境でも確認してみる。

[yamamoto@my-linux]% crontab -e
## 適当にcronを作成。
* * * * * echo "hello" >> /home/yamamoto/python/cronTest.txt

[yamamoto@my-linux]% crontab -l
* * * * * echo "hello" >> /home/yamamoto/python/cronTest.txt
## 登録されていることを確認

[yamamoto@my-linux]% ls /var/spool/cron
ls: cannot open directory '/var/spool/cron': Permission denied

おっと。どうやら/var/spool/cronは一般ユーザー権限だと見られないらしい。「編集はできるのに見られないとはどういうこっちゃ!」と思うが、一旦それは置いておこうか。ここは黙って管理者権限で確認していく。

[yamamoto@my-linux]% sudo -i
# 管理者権限に昇格

[root@my-linux]% ls -al /var/spool/
...
drwx------.  2 root root 4096 XXX XXX XX:XX cron
...
# rootしかアクセスできないようになってやがらあ。

[root@my-linux]% cat /var/spool/cron/yamamoto
* * * * * echo "hello" >> /home/yamamoto/python/cronTest.txt
# 発見!!

無事見つかった。つまり、このファイルを書き換えちゃえばcrontabは制御できるということだ。

ちなみに、「編集はできるのに見られないとはどういうこっちゃ!」の問題については、crontabコマンドがSUIDによりroot権限で実行されるようになっているため。なるほど勉強になるなあ。
詳しくは以下を参照。

pythonでcrontabの実装

pythonでcrontabを触ってみようと思うのだが、、、

windowsでいきなりcronをいじるのはちょっと怖い。。。(ていうかあるの??)

windowsのWSLにはcrontabコマンドがあるので、できそうではあるのだが、いきなりここでいじるのは憚られるので、今回は仮想マシンのLinux (fedora) でやっていこうと思う。(↑の crontab の確認でも出てきたOSと同じ)

python-crontabとOSのcrontab

python-crontab というものがあるらしく、pythonでプログラムの自動実行を管理できるらしい。の使い方は以下のページを参照。

最初はこちらを使ってあれこれできたら、、、と思っていたのだが、どうやらこれはcrontabをpythonで完結させるものらしい。ヤマモトも勘違いしていたのだが、OS標準搭載のcrontabには一切関与しないっぽい。

仕様を説明すると、pythonで独自のcronを設定し、プログラムのスケジュール設定を行う。あとはpythonがずっと稼働状態で監視を続けて、時間になったら設定したプログラムを動かすというものらしい。つまるところ、監視状態とはいえ、pythonがずっと動いている状態なわけだ

ヤマモト的には「OSのcrontabを編集するモジュールなのかな~」と思っていたのだが、どうやらそうでも無いらしい。

う~ん。監視のためだけにpython動かしておくのもなんかイマイチな気がする。。。ていうかそれならOSのcronで事足りるよなあ。

ということで、方針としては「OSのcrontabをpythonで編集する」に変えようと思う。

pythonでcrontabの編集をやってみる

まずは以下の2つのpythonコードを用意する。

  • testHello.py ← cronで実行されるプログラム。定時になったらひたすらHelloと吐き続ける。
  • testCrontab.py ← crontabを書き換えるプログラム。testHello.pyをcrontabに仕込む役割。

testHello.pyはまあ適当に作る。

import datetime

dt_now = datetime.datetime.now()

print(f"Hello {dt_now}")

さて、問題はtestCrontab.pyの方。

crontabのファイルの場所はわかったが、前述の通り一般ユーザー権限では直接ファイルを編集するどころか参照することも許されていない。

無論、ディレクトリの権限を chmod とかで書き換えちゃえばできちゃうわけだが、、、OSの構造を書き換えると予想外のところで不具合が生じる可能性があるので、やめておく。
(大いなる力には大いなる責任が伴う、ということ。)

となると、消去法で crontab コマンドをpythonで実行するのが無難そう。

コードを書いてみる。

pythonでOSコマンドの実行といえば subprocess だろうか。os もあるが、以下記事の通り今は subprocess が主流っぽいので、こっちを使おう。

また、crontab-e で編集モードに移ってしまうが、それ用の書式で書かれたファイルを引数に渡してあげると、編集モードに行かずに変更が可能。

それ用の書式については python-crontab モジュールが得意なので、こちらはモジュールに任せてみようと思う。(結局モジュール使うんかい)

早速コードを書いてみる。

# モジュールのインポート
from crontab import CronTab
import subprocess

# crontabに書き込む書式の作成
cron = CronTab()
job = cron.new(command='python /home/yamamoto/python/testHello.py >> /home/yamamoto/python/testHello.txt')
job.setall('* * * * *')
# 書式を出力
cron.write('/home/yamamoto/python/testPython.tab')

# 出力したcrontabの書式を引数にcrontabを実行
command = ["crontab", "/home/yamamoto/python/testPython.tab"]
subprocess.call(command)

実際に実行してみる。

(py310)[yamamoto@my-linux]% python testCrontab.py 
(py310)[yamamoto@my-linux]% crontab -l           
* * * * * python /home/yamamoto/python/testHello.py >> /home/yamamoto/python/testHello.txt
# cronの書込みに成功!!

(py310)[yamamoto@my-linux]% cat testHello.txt 
Hello 2022-09-23 20:00:05.540600

# ちゃんとcronが動いている!!

おおおおおお。良い感じ。これなら簡単かつ自動でcrontabを編集できそうだな。

ここまでやってみて気づいたこと…

はい、順風満帆で何より。

・・・と言いたいところなのだが、ふと冷静になって考えてみた。

crontab、っていうかcronってそもそも、定期実行のタスクスケジューラーだよなあ
よくよく考えたら、当初の目的を踏まえると、ジャンプの発売日って定期実行ではなくない??

・・・ガッッッッッッッッッッッデム!!!!!!!

ヤマモトの頭の中では、ジョブの自動実行 = cron って勝手に変換されていたのだが、よくよく考えたらやりたいことにマッチしてないじゃん!! イマイチじゃん!!!

調べてみると、ちゃんと整理されてた。なるほど。未来日付で一回だけ実行するなら、at を使うと良いのね。

ということで、次回、at コマンドでジョブスケジュールを設定してみた」。
乞うご期待!!!!

マジで何の時間だったんだ… orz



コメント

タイトルとURLをコピーしました