普段業務はWindows上で行っています。Python何かを動かすときはLinux上であることが多いですが、たまにWindowsで、ジョブを動かさないといけないケースもあります。今回、ちょっとWindows上でプロセス一覧を取得しないといけないケースがあったので行った内容をまとめます。
対象環境
今回は以下の環境で実施しました。
- Windows 7 64bit
- Python 3.6
Windowsでプロセス一覧取得
Linuxでのps aux
的な情報が欲しかったのですが、tasklist
ではexeまでしか取れずプロセスの引数等が取れませんでした。色々確認していったところ、どうやらwmic process
で欲しい情報がとれることがわかりました。
wmic process
はプロセスの引数だけでなく、そのプロセスの状態や統計等をSQLのように取得できるようですが一旦全データを取得できる以下のコマンドを使うことにしました。
DOS> wmic process get /FORMAT:LIST |head -n 20 Caption=System Idle Process CommandLine= CreationClassName=Win32_Process CreationDate=20170911132520.625200+540 CSCreationClassName=Win32_ComputerSystem CSName=B1609-21-01 Description=System Idle Process ExecutablePath= ExecutionState= Handle=0 HandleCount=0 InstallDate= KernelModeTime=4151831986137 MaximumWorkingSetSize= MinimumWorkingSetSize= Name=System Idle Process OSCreationClassName=Win32_OperatingSystem OSName=Microsoft Windows 7 Professional |C:\Windows|\Device\Harddisk0\Partition3
出力結果は扱いづらいですが、commandline
等にとりあえず欲しい情報が含まれていることがわかりましたのでこれをPythonでラップしていきます。
出力結果について
この出力結果のフォーマットは以下でした。
- 各行は
key=value
の形式で出力される - プロセスは空行で区切られている
割りとシンプルなフォーマットなのでパースするのは簡単ですね。
processを示すクラス
Dictでそのまま使ってもいいですが、とりあえず結果をしまうクラスを用意しました。
class WindowsProcess(object): """ Windows上でのwmic processでの結果に紐づくクラス """ def __init__(self, attributes): """ sample: caption python.exe commandline python -m unittest test_OsUtil.TestOsUtils.test_01_get_processes creationclassname Win32_Process : writetransfercount 4800 :param attributes: """ for k, v in attributes: # Pythonはlowerなので揃える setattr(self, k.lower(), v) def __str__(self): display_name = self.commandline if self.commandline else self.caption return "Process[{} {} {}]".format( self.parentprocessid, self.processid, display_name ) def __repr__(self): return self.__str__()
本当は各キーの属性を先に用意するべきなのですが、ちょっと数が多い上にほとんど使わないと思ったのでsetattr
で動的に設定するようにしてしまいました。とりあえず見たいのはparentprocessid
(親プロセスID)やprocessid
(プロセスID)、commandline
(コマンドライン引数)ですので十分だと思います。
wmicのラッパー
先程のコマンドをPythonでラップします。subProcess.run
で実行した結果素直に一行ずつ回して空行かきたらWindowsProcessを作っていく流れになっています。
import subprocess import sys import os def get_processes(): """ プロセス一覧をwmic経由で取得する :param process_name: :return: """ encoding = sys.stdout.encoding if not encoding: encoding = "UTF-8" # wmic process get /FORMAT:LIST command_str = " ".join([ "wmic", "process", "get", "/FORMAT:LIST" ]) result = subprocess.run(command_str, shell=True, stdout=subprocess.PIPE) if not result.stdout: return [] # 1行目はヘッダなので。 process_list = [] buf = [] for line in result.stdout.decode(encoding).split("\r\r\n"): # 結果はプロセスごとに空行が挟まっている if line == "": if buf: target_process = WindowsProcess(buf) process_list.append(target_process) buf = [] continue # コマンドの引数自体に=があるケースに備える key = line.split("=")[0] value = "=".join(line.split("=")[1:]) buf.append([key, value]) return process_list
まとめ
一応作ったスクリプトを全文載せておきます。あとは適当に属性を見ながらフィルタをPythonでかけて使えると思います。
# codingt:utf-8 import subprocess import sys import os class WindowsProcess(object): """ Windows上でのwmic processでの結果に紐づくクラス """ def __init__(self, attributes): """ :param attributes:[ [key, value], [key, value], ] """ for k, v in attributes: # Pythonはlowerなので揃える setattr(self, k.lower(), v) def __str__(self): display_name = self.commandline if self.commandline else self.caption return "Process[{} {} {}]".format( self.parentprocessid, self.processid, display_name ) def __repr__(self): return self.__str__() def get_processes(): """ プロセス一覧をwmic経由で取得する :param process_name: :return: """ encoding = sys.stdout.encoding if not encoding: encoding = "UTF-8" # wmic process get /FORMAT:LIST command_str = " ".join([ "wmic", "process", "get", "/FORMAT:LIST" ]) result = subprocess.run(command_str, shell=True, stdout=subprocess.PIPE) if not result.stdout: return [] # 1行目はヘッダなので。 process_list = [] buf = [] for line in result.stdout.decode(encoding).split("\r\r\n"): # 結果はプロセスごとに空行が挟まっている if line == "": if buf: target_process = WindowsProcess(buf) process_list.append(target_process) buf = [] continue key = line.split("=")[0] value = "=".join(line.split("=")[1:]) buf.append([key, value]) return process_list if __name__ == "__main__": for proc in get_processes(): print(proc)
実行例
(py36_mm) > python ps.py |findstr ps.py Process[6644 3344 python ps.py ] Process[6644 15768 findstr ps.py]
ちゃんと思った結果になってますね^^