その他
    ホーム 技術発信 DoRuby pythonでコマンドを扱う
    pythonでコマンドを扱う
     

    pythonでコマンドを扱う

    この記事はアピリッツの技術ブログ「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”だと実行時のパスが一致しないので、絶対パスを取得してやる必要がある。