この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
pythonで標準入力と標準出力を扱います。
概要
pythonに慣れていて手軽に書けるので、他の言語でできることでもなるべくpythonでやりたいです。
ローカルマシンのプロセスをダンプする必要があったんですが、シェルスクリプトを書きたくなかったのでpythonで書きました。
コード
pythonのバージョンは3.6.1です。
import subprocess
import shlex
import os
from datetime import datetime
command = "ps alx"
log_file = os.path.dirname(os.path.abspath(__file__)) + "/process.log"
def main():
buf_list = []
buf_list.append(get_current_time())
for i in readline_stdout():
buf_list.append(i)
write_file_to_log(buf_list)
# 現在時刻を取得
def get_current_time():
current_time = datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " ---------------------------------------------------------------------------------\n"
return current_time
# 標準出力を一行ずつ受け取る
def readline_stdout():
proc = subprocess.Popen(shlex.split(command), stdout = subprocess.PIPE, stderr = subprocess.PIPE)
while True:
line = proc.stdout.readline()
yield line.decode("utf-8")
if not line and proc.poll() is not None:
break
# ファイルに出力
def write_file_to_log(logs):
file = open(log_file, 'a')
file.writelines(logs)
if __name__ == "__main__":
main()
# 実行結果の一部
2017/11/22 18:00:22 ---------------------------------------------------------------------------------
UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TT TIME COMMAND
0 1 0 0 37 0 2536056 6748 - Ss ?? 1:00.50 /sbin/launchd
0 50 1 0 37 0 2550352 37604 - Ss ?? 0:08.14 /usr/libexec/UserEventAgent (System)
0 51 1 0 4 0 2515384 2672 - Ss ?? 0:01.24 /usr/sbin/syslogd
0 53 1 0 20 0 663372 1216 - Ss ?? 0:00.56 /Applications/.esets/Contents/MacOS/esets_ctl
0 54 1 0 20 0 2498064 10000 - Ss ?? 0:01.52 /System/Library/PrivateFrameworks/Uninstall.framework/Resources/uninstalld
0 55 1 0 37 0 2546936 12252 - Ss ?? 0:02.51 /usr/libexec/kextd
ポイント
ポイントは2つです。
1. subprocessを使って標準入力と標準出力を扱う
2. shlexを使って文字列をシェルスタイルでパースする
subprocessを使って標準入力と標準出力を扱う
pythonでコマンドを扱う方法はcmdやos.system()など複数ありますが、subprocessを使うのがスタンダードなやり方っぽいです。
shlexを使う
今回のスクリプトを書いている中での最大の学びはshlexです。
たとえば、今回のコマンドは”ps alx”をそのまま渡してるんですが、shlex.split()を使わない場合は、[“ps”, “alx”]としてやる必要があります。
長いコマンドを処理したい時は面倒なので、shlexを使ってやると非常に便利です。
追記
shlex.splitを使わなくても、subprocess.Popemの引数にshell = True
パラメーターを与えてやると"ps alx"
でもうまくいきます。shell = True
パラメーターで処理をする利点は、"ps alx | grep hoge
みたいにパイプ(“|”)を使ったコマンドが利用できるということです。
shlex.splitでパイプの入ったコマンドを利用しようとすると上手く処理できません。
追記その2
出力先ファイルのパス指定を修正しました。
cronで回したときに”process.log”だと実行時のパスが一致しないので、絶対パスを取得してやる必要がある。