Pythonでディレクトリ監視

投稿者: | 2020年12月3日

現在バイト先の業務を自動化(半自動化)するために色々ツール作成しています。現在、pdfファイルを纏めて処理系に投げ、必要な情報を返してもらうようなものを作成しています。

バイト先の基幹ツールではpdfファイルは1日おきにしか出力できず、さらに全てのファイル名が同じのため、pdfファイルを一つのディレクトリにまとめられません。そこで、ディレクトリを監視し、ファイルを自動的にリネームするツールを作成しようと考えました。

今回は、ディレクトリを監視をするところまで実装しています。

1.実装

1-1.環境

  • windows10 (build 1909)
  • Python 3.7.2
  • pip 20.3

1-2.準備

watchdogライブラリを用いるので、pipを用いてインストールします。

pip install watchdog

1-3.コード

【Python】watchdogでファイルやフォルダを監視するを参考に実装しました。

2.調べたこと

pythonを書くのが久しぶりなので、色々調べながら書いていました。そのあたりをメモしていきます。

2-1.watchdogとは?

ファイルシステムイベントを監視するPython APIのようです。

watchdog 0.10.4

イベントハンドラとオブサーバーを使ってファイル監視を行います。4つコールバック関数がイベントハンドラクラスに定義されています。

  • on_created
  • on_deleted
  • on_modified
  • on_moved

これらと、

  • on_any_event

の5つのメソッドがあります。ファイル操作に応じて実行するコードを実装できます。

2-2.hashlib

hashlib — セキュアハッシュおよびメッセージダイジェスト

SHA-1,SHA224,SHA256,SHA384,SHA512,MD5を実装しているらしい。BLAKE2も実装している。

ただ、これの実装には必要ないので削除。

2-3.文字フォーマット

今回は、定型文+ファイル名というものを標準出力させています。このファイル名は毎回変わるわけですが、これを実現するためにformatメソッドを用いました。

string — 一般的な文字列操作

もとのコードは%構文を使っていましたが、それは廃止になるようです。(%構文でググっても出てこないんですが・・・)

formatメソッドですが、次のようになります。

from datetime import datetime
#今日の日付を取得
today = datetaime.now().date()
#formatメソッドを使用
print('today:{}'.format(today))

中括弧の部分を置換フィールドといい、format()メソッドの引数で中括弧部分を置換します。ちょうどsnippetのような感じです。

これには複数の引数を取ることができるようです。あと色々できるみたい。C++のstd::fixedのようなこともできるっぽい。

2-3.__file__

python3コマンドで指定したパスが取得できます。今回だと、実行しているスクリプトファイルのパスが取得できます。

同じようなものが、os.getcwd()があり、こちらはPythonが実行されているカレントディレクトリを絶対パスとして文字列で返してくれます。

去年ファイルを開くプログラムをPython作っていたときに、pathを絶対パスで指定する場合、windowsだと

path = r"G:\atom\igarashi\counter_round10.txt"

のように先頭にrをつけないとうまく行かないのを思い出しました。

2-4.if __name__ == “__main__”

pythonを書くときのおまじないらしいです。

調べてみると、モジュールをインポートしただけで中身が実行されることを防ぐために書くようです。

Pythonのif __name__ == "__main__" とは何ですか?への回答

ここはシングルクォーテーションでもダブルクオーテーションでも同じになると思ったら、答えが書かれていた。

なお、Pythonでは文字列リテラルをシングルクォート'でもダブルクォート"でも表せるので、if __name__ == "__main__"でも同じ意味。

C++だとchar型のをダブルクオーテーションで囲むと怒られるし、string型のをシングルクォーテーションで囲むと怒られるのに、Pythonは寛大です。

参考:Pythonのif name == ‘main‘の意味と使い方

2-5.sleep()

ある一定プログラムを止めるときに使う関数です。引数に設定した時間の間プログラムが停止します。単位は秒。

競プロじゃ使わない要素だから、こんなのあるんだーって思いました。

2-6.Pythonの例外処理

try~exceptを使うらしい。C++だとtry~catchになります。同じように使っても大丈夫そう。

Pythonの例外処理(try, except, else, finally)

2-7.組み込み例外:KeyboardInterrupt

exception KeyboardInterrupt
ユーザが割り込みキー (通常は Control-C または Delete) を押した場合に送出されます。実行中、割り込みは定期的に監視されます。Exception を捕捉するコードに誤って捕捉されてインタプリタの終了が阻害されないように、この例外は BaseException を継承しています。

参考:組み込み例外

0除算したときに飛ぶ例外なども組み込み例外で定義されています。

2-7.Pythonにおけるbool型

真偽値ですが、True,Falseと最初を大文字で書かないとだめみたいです。ただし、bool型はint型のサブクラスなので、Trueを1、Falseを0と書いてもOKです。

いつもC++でtrue,falseと小文字で書いてたから、これ駄目なんだと驚いた。

3.実行結果

START Script
c:\Users\ateruimashin\OneDrive\program\tools\folder-serch
[MAKE]:新しいテキスト ドキュメント.txt
[MOVE]:新しいテキスト ドキュメント.txt
[MAKE]:250px-Salsa20_ChaCha_variant.png
[MODI]:250px-Salsa20_ChaCha_variant.png
[MODI]:250px-Salsa20_ChaCha_variant.png
[MODI]:250px-Salsa20_ChaCha_variant.png
[MAKE]:cap.PNG
[MODI]:cap.PNG
[MODI]:cap.PNG
[MODI]:test.txt
[MODI]:250px-Salsa20_ChaCha_variant.png
[DEL]:250px-Salsa20_ChaCha_variant.png
[DEL]:cap.PNG
[DEL]:test.txt
#Ctrl+Cを押下
FINISH Script

別フォルダから移動してきた場合は[MAKE]になり、リネームした場合は[MODI]になっています。

windowsの場合はリネームなどした場合[MODI]が2回出力されるのは仕様らしいです。

まとめてファイルを削除しても全て[DEL]になります。

4.おわりに

昨日風呂に入りながらC++で実装しようと考えていたのですが、調べていると記事が2015年やそれより古いのばっかりだったので、Pythonで書くことにしました。

そして、B4のゼミ発表をしているのを聞きながら書いていたら、一人目の発表の時間でここまで完成してしまいました。Pythonは去年書いたっきり触ってなかったのに、ぐぐったらめちゃくちゃ関連記事が出てくるので全く悩まずにデバッグ作業も行えました。エラーメッセージが具体的なのもいいですね。

あとは、ファイルをリネームする処理を入れて、exe化したらこのツール作成は終わりです。

私のバイト先は残念ながら非IT関係なので、PCにPythonなんて入ってないんですよね。だからexe化します。めんどくせえ。

それと、使用者も非IT関係なので、もうちょっとわかりやすくしたいかな。特に終了にCtrl+C押すというのはわかりにくいですし。まあ、ウィンドウの☓押してくれれば終了しますけど。

ここまで書いてて、今考えている業務ツールを全部pythonで書いてexe化すればよくないか?となっています。バイト先のネットワーク回線が細すぎるのもネックだったので・・・。

とりあえずリネーム処理だけ作ってこのツールを完成させます。

5.参考文献

watchdog

Python WatchDog – フォルダの監視を行う

Python ファイルを監視してプログラムを実行したいとき

【Python】watchdogでファイルやフォルダを監視する

Pythonで実行中のファイルの場所(パス)を取得する__file__

Pythonの文字列フォーマット(formatメソッドの使い方)

[Python] プログラムを一定時間停止させる(time.sleep)

string — 一般的な文字列操作

Python日付型

Pythonでカレントディレクトリを取得、変更(移動)

Pythonのif name == ‘main‘の意味と使い方

Pythonの例外処理(try, except, else, finally)

組み込み例外

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください