LoginSignup
2
0

More than 1 year has passed since last update.

日の入りで指定のコマンドを実行するPythonスクリプト

Last updated at Posted at 2022-03-19

日の入りのタイミングで夕焼け小焼けを再生したかったが、
動的に crontab を書き換えるのも気持ち悪く、at コマンドも使えなかったので、
かわりにPythonで実装した記録。

やりたいこと

runat.py sunset play -q yuyakekoyake.mp3
とすると、日の入りのタイミングで play -q yuyakekoyake.mp3 を実行する。
あとは、このコマンドをcrontabに正午くらいにでも放り込んでおけばよい。

つまずき・実装のポイント

argparse でコマンドを受け取る

引数解析はもちろん argparse にやらせるが、前述の例だと -q もオプション引数と認識されてしまい、unrecognized arguments エラーが出る。
これを回避するには、コマンドを受け取る add_argument メソッドに nargs = argparse.REMAINDER を指定する。
cf. https://docs.python.org/ja/3.7/library/argparse.html#argparse-remainder

日の出の時刻の計算

ephem モジュールにお任せしました。
https://pypi.org/project/ephem/

外部コマンドの実行

Pythonに渡された時点でダブルクォート等の空白処理はなされているので、
絶対に os.system(' '.join(args.command)) なんてことはしないこと!
subprocess.run 関数にリストを渡せるので、これを使いましょう。

以下、作ったスクリプト

runat.py
#!/usr/bin/env python
#encoding : utf-8

import sys
import argparse
import logging
import datetime
import time
import subprocess

try:
    import ephem
except ModuleNotFoundError:
    sys.exit (1)

class choice:
    def __init__ (self, star = None, attr = None):
        self.star = star
        self.attr = attr

CHOICES = {
        'sunrise' : choice (star = ephem.Sun,  attr = 'next_rising' ),
        'sunset'  : choice (star = ephem.Sun,  attr = 'next_setting'),
        'moonrise': choice (star = ephem.Moon, attr = 'next_rising' ),
        'moonset' : choice (star = ephem.Moon, attr = 'next_setting'),
        }

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument ('--city', default = 'Tokyo')
    parser.add_argument ('--longitude')
    parser.add_argument ('--latitude')
    parser.add_argument ('--elevation', type = int)
    parser.add_argument ('at', choices = CHOICES.keys() )
    parser.add_argument ('command', nargs = argparse.REMAINDER)
    args = parser.parse_args()
    logging.debug ('args = %s' % args)

    place = ephem.city (args.city)
    if args.latitude is not None:
        place.lat = args.latitude
    if args.longitude is not None:
        place.lon = args.longitude
    if args.elevation is not None:
        place.elevation = args.elevation

    logging.debug ('place = %s' % place)

    star = CHOICES[args.at].star()
    timeat = getattr(place, CHOICES[args.at].attr) (star)
    delta = timeat.datetime() - datetime.datetime.utcnow()

    logging.debug ('going at %s' % ephem.localtime(timeat))

    try:
        time.sleep (delta.seconds)
        subprocess.run (args.command)
    except KeyboardInterrupt:
        sys.exit(1)

if __name__ == '__main__':
    main()

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0