Python: コマンド実行と標準出力の文字列操作

Pythonスクリプトで、外部コマンド(unix/linux/cygwinのコマンド)を実行し、その結果(stdoutに出力された文字列)を解析して情報を取得したいことがある。例えば、

  • ある処理を実行したときのログファイルが多量にある
  • 各ログファイルには決まったフォーマットで、実行時間の情報が1行書かれている
    [例] time: 123.4
  • それらの実行時間の情報を一覧にしたCSVファイルを出力する

というケースである。

ここで、全ての処理をPythonで書くことも可能だが、実行時間の情報が書かれた1行を取り出すのに、grepを使ってみる。Pythonスクリプトの処理フローは以下だ。

  1. カレントディレクトリにあるログファイル(*.log)のリストを作成
  2. grep time: *.logを実行し、標準出力の内容を変数に取得
  3. “ログファイル名: 実行時間”という形式でCSVファイルに出力する

Pythonスクリプト cmd_grep.py の内容は以下の通り。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: fileencoding=euc-jp

import os, sys, re, glob, subprocess
import pandas as pd

fname_out = 'grep.csv'

def get_file_list(target='*.log', dir_name=""):

if dir_name == "":
data_dir = "."
else:
data_dir = dir_name

return sorted(glob.glob(os.path.join(data_dir + '/' + target)))

"""--------------------------------------
main
--------------------------------------"""
logfiles = get_file_list(target='*.log', dir_name='.')
print('found ' + str(len(logfiles)) + ' files')

cmd = ['/bin/grep', 'time:'] + logfiles
result = subprocess.run(cmd, stdout=subprocess.PIPE)
lines = result.stdout.splitlines()
print('found ' + str(len(lines)) + ' results')

log_list = []
elaps_list = []

for l in lines:
s = re.search('([\.\w]+):\s*time:\s+([\d\.]+)', str(l))
if s:
log = s.group(1)
elaps = float(s.group(2))
print("{}: {:6.1f}".format(log, elaps))

log_list.append(log)
elaps_list.append(elaps)

"""--------------------------------------
output CSV
--------------------------------------"""
df = pd.DataFrame()
df['log'] = pd.Series(log_list)
df['time'] = pd.Series(elaps_list)
df.to_csv(fname_out)

sys.exit(0)

ログファイルの例 time1.log~time3.logの中身は以下とする。

$ more time*.log
::::::::::::::
time1.log
::::::::::::::
hogehoge
time: 123.4
::::::::::::::
time2.log
::::::::::::::
foobar
time: 456.7

::::::::::::::
time3.log
::::::::::::::
xyz
time: 89.01

実行時の画面表示は以下のようになる。

$ ./cmd_grep.py
found 3 files
found 3 results
time1.log: 123.4
time2.log: 456.7
time3.log: 89.0

そして、Pythonスクリプトが出力したCSVファイルは以下のようになる。

$ more grep.csv
,log,time
0,time1.log,123.4
1,time2.log,456.7
2,time3.log,89.01

Pythonを覚えて間がないので、もっとスマートな書き方があるかもしれないが、まずはこれで目的を達成できた(そもそも、Pythonでgrepを呼び出さず、テキストファイルの中身をre.search()で走査するので十分だが、あえて外部コマンドを呼んだ)。

Python

Posted by ゆたぱ