4
4

More than 3 years have passed since last update.

TOPコマンド結果をUSERで抽出してCSV出力する

Last updated at Posted at 2020-07-09

はじめに

様々な理由から、LinuxのTOPコマンドでのパフォーマンス測定を余儀なくされている同志の方々に送ります。

目的

LinuxのTOPコマンドをバッチモードで出力したファイルを、
python3を使ってcsvファイルに整形します。

top.csv(出力例)
timestamp,PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,COMMAND
10:00:00,1000,root,20,0,160000,2000,1640,R,10.0,0.2,0:00.02,top
10:00:00,3400,httpd,20,0,150000,2000,1700,S,0.0,0.3,0:07.98,nginx:

用意するもの

TOPのバッチモードで出力したファイルを用意します

TOPコマンドファイル
top -b -d 20 -c > top_org.log
top_org.log
top - 10:00:00 up 1 days, 44 min,  2 users,  load average: 0.00, 0.01, 0.01
Tasks: 100 total,   1 running, 99 sleeping,   0 stopped,   1 zombie
%Cpu(s):  2.0 us,  4.0 sy,  0.0 ni, 80.0 id,  5.0 wa,  0.0 hi,  2.0 si,  0.0 st
KiB Mem :  1000000 total,    60000 free,   700000 used,   200000 buff/cache
KiB Swap:  2000000 total,    90000 free,  2000000 used.    70000 avail Mem 

PID   USER     PR   NI VIRT     RES    SHR  S  %CPU %MEM  TIME+    COMMAND
1000 root      20   0  160000   2000   1640 R  10.0  0.2   0:00.02 top -b -d 20 -c
4500 apache    20   0  440000   1000      8 S   0.0  0.1   0:00.01 /usr/sbin/httpd
17000 mysql     20   0 1130000   7000      0 S   0.0  0.7  20:00.00 /usr/sbin/mysqld
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.00 [kthreadd]
    4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kworker/0:0H]
    6 root      20   0       0      0      0 S   0.0  0.0   0:00.00 [ksoftirqd/0]
・・・(以下略)

お急ぎの方へ

本記事末尾に、分割されていないソースコードがあります。そちらを参照下さい。
その際、以下の部分だけは変更して下さい。

毎回変更する部分

・抽出対象のUSER
・「TOPコマンドファイル」の場所
・「TOPコマンドファイルを整形したCSVファイル」の場所
を以下の部分で設定しています。
希望条件や、環境に合わせて変更してください。

.py
'''
設定情報
'''
# 抽出するUSER(未設定だと全USERで抽出。,区切りで設定)
user_array =['root'] 

# カレントDir取得
current_dir = os.getcwd()
# inputファイル名(Full PATH)
input_file_name=f"{current_dir}\\before\\top_org.log"
# outputファイル名(Full PATH)
output_file_name=f"{current_dir}\\after\\top_csv.csv"

ソースコード

解説

  1. 必要情報の設定
  2. TOPコマンドファイルの読み込み
  3. 先頭カラムに使うtimestampを抽出
  4. 対象USERのプロセスを抽出
  5. CSVファイル出力

という順番で進んでいきます。
 

1. 必要情報の設定

抽出対象となるUSERや、TOPコマンドファイルが配置している場所の指定を行います。

.py
# -*- coding: utf-8 
import re
import os
import csv

'''
設定情報
'''
# 抽出するUSER(未設定だと全USERで抽出。,区切りで設定)
user_array =['root'] 

# カレントDir取得
current_dir = os.getcwd()
# inputファイル名(Full PATH)
input_file_name=f"{current_dir}\\before\\top.log"
# outputファイル名(Full PATH)
output_file_name=f"{current_dir}\\after\\top.log"

# 1回のTOPコマンド結果の中でプロセス行の始まりは何行目か
process_row_start = 8
# TOPコマンド結果でUSERカラムの位置は何カラム目か
user_column = 2
# TOPコマンド結果でCOMMANDカラムの位置は何カラム目か
command_column = 12

 

2. TOPコマンドファイルの読み込み

.py
    # inputファイル読み込み
    f = open(f"{input_file_name}", "r")
    toplog_lines = f.readlines()
    f.close()

inputファイル(TOPコマンドファイル)を、1行ずつ読み込んで変数toplog_linesに格納します。

3. 先頭カラムに使うtimestampを抽出

.py
    # timestamp正規表現
    r_top_timestamp = re.compile("top - ([0-9:]+)+")

    timestamp_list = []
    roop_cnt = 0
    for toplog_line in toplog_lines :
        # 正規表現でtimestamp一致したらTOPコマンドファイルの行番号を行番号を配列に入れる
        if r_top_timestamp.search(toplog_line) != None:
            timestamp_list.append(roop_cnt)
        roop_cnt += 1

ここでは、timestamp(top - 10:00:00 up 1 days, 44 min, 2 users, load average: 0.00, 0.01, 0.01)が、
TOPコマンドファイルの何行目に入っているのか確認し、timestamp_listに行番号を格納しています。

4. 対象USERのプロセスを抽出

まずは、プロセス抽出ループの中で必要な変数を定義します。

.py
# TOPコマンドでプロセス行の始まりの位置
process_row_start = 8
# TOPコマンドでプロセス行のUSERカラムの位置
user_column = 2
# TOPコマンドでプロセス行のCOMMANDカラムの位置
command_column = 12
# 1回のTOPコマンド行数カウント用変数
rows_count = 0
# timestamp格納用変数
tmp_timestamp = ''
# csvに出力する文字列を格納するための配列(tmp)
tmp_output_csv_list = []
# csvに出力する文字列を格納するための配列(実際の書き出しに利用)
output_csv_list = []

 
次はTOPコマンドファイルを1行ずつループします。
以下を実施しています。
・TOPコマンド実施時間(タイムスタンプ)を抽出
・プロセスを1カラムずつ確認し抽出対象USERであれば、csv出力対象とする

.py
    for toplog_line in toplog_lines :
        # 1回のTOP行数カウント +1
        rows_count +=1
        # 行末尾の改行を削除
        toplog_line = toplog_line.rstrip()

        # 改行のみの場合、次の行へ
        if not toplog_line :
            continue

        # タイムスタンプ行の場合、リストに追加し、次の行へ
        if r_top_timestamp.search(toplog_line) != None:
            print(toplog_line)
            tmp_timestamp = r_top_timestamp.search(toplog_line).group(1)
            # 1回のTOPコマンド結果、行数カウント初期化
            rows_count = 1
            continue

        # プロセス行の場合、Userが一致すれば抽出する
        if rows_count >= process_row_start:
            column_number = 0
            row_data = toplog_line.split(" ")
            # タイムスタンプを設定
            tmp_output_csv_list = [tmp_timestamp]

            # 行末まで繰り返す
            for column_data in row_data:
                if column_data =="":
                    # 空白の場合次のカラムへ
                    continue
                column_number += 1

                # COMMANDカラムまでのデータをtmpリストに入れる
                if column_number <= command_column :
                    tmp_output_csv_list.append(column_data)
                else :
                    continue

                # 抽出対象レコードかをチェックする
                # 抽出対象USER、または、user_array指定なしのとき抽出対象レコード選定
                if column_number == user_column :
                    user_key_flg = True
                    for key_user in user_array:
                        # 抽出対象USER、または、user_array指定なしのとき抽出対象レコード
                        if ( str(column_data) == key_user ):
                            user_key_flg = True
                            break
                        else:
                            user_key_flg = False

                    if user_key_flg == True :
                        pass
                    else:
                        break
            # for-else :条件を満たしたときのみ、CSV抽出リストに追加する
            else:
                output_csv_list.append(tmp_output_csv_list)

5. CSVファイル出力

csv抽出リスト output_csv_list の中身をcsvに出力します。

.py
    # CSV書き出し
    csv_header = ['timestamp','USER','PR','NI','VIRT','RES','SHR','%CPU','%MEM','TIME+','COMMAND']
    with open(f'{output_file_name}','w') as f:
        csv_writer = csv.writer( f, delimiter = ',', lineterminator = '\n') 
        csv_writer.writerow(csv_header)
        csv_writer.writerows(output_csv_list)   

csv_writer = csv.writer( f, delimiter = ',', lineterminator = '\n')の、
delimiter = ',' を、
delimiter = '\t' にすれば、tsvファイル(タブ区切りファイル)にもなります。
お好みでどうぞ。

ソースコードまとめ

.py
'''
Topコマンドのプロセス行をUSERで抽出しcsvファイルに出力する
timestamp,USER,PR,NI,VIRT,RES,SHR,%CPU,%MEM,TIME+,COMMAND
'''
import re
import os
import csv

'''
設定情報
'''
# 抽出するUSER(未設定だと全USERで抽出。,区切りで設定)
user_array =['apache','httpd']

# カレントDir取得
current_dir = os.getcwd()
# inputファイル名(Full PATH)
input_file_name=f"{current_dir}\\before\\top.log"
# outputファイル名(Full PATH)
output_file_name=f"{current_dir}\\after\\top.log"


# TOPコマンドでプロセス行の始まりの位置
process_row_start = 8
# TOPコマンドでプロセス行のUSERカラムの位置
user_column = 2
# TOPコマンドでプロセス行のCOMMANDカラムの位置
command_column = 12

# timestamp正規表現
r_top_timestamp = re.compile("top - ([0-9:]+)+")

######## main 処理 #############
if __name__ == '__main__' :
    '''----------------------
       toplogファイル読み込み
    ----------------------'''
    # inputファイル読み込み
    f = open(f"{input_file_name}", "r")
    toplog_lines = f.readlines()
    f.close()

    '''----------------------
       timestamp行の抽出
    ----------------------'''
    timestamp_list = []
    roop_cnt = 0
    for toplog_line in toplog_lines :
        # 正規表現でtimestamp一致したら行番号を配列に入れる
        if r_top_timestamp.search(toplog_line) != None:
            timestamp_list.append(roop_cnt)
        roop_cnt += 1

    '''--------------------------
       対象Userのプロセスを抽出
    --------------------------'''
    rows_count = 0
    tmp_timestamp = ''
    tmp_output_csv_list = []
    output_csv_list = []

    for toplog_line in toplog_lines :
        # 1回のTOP行数カウント +1
        rows_count +=1
        # 行末尾の改行を削除
        toplog_line = toplog_line.rstrip()

        # 改行のみの場合次の行へ
        if not toplog_line :
            continue

        # タイムスタンプ行の場合、リストに追加する
        if r_top_timestamp.search(toplog_line) != None:
            print(toplog_line)
            tmp_timestamp = r_top_timestamp.search(toplog_line).group(1)
            # 1回のTOPコマンド結果、行数カウント初期化
            rows_count = 1
            continue

        # プロセス行の場合、Userが一致すれば抽出する
        if rows_count >= process_row_start:
            column_number = 0
            row_data = toplog_line.split(" ")
            # タイムスタンプを設定
            tmp_output_csv_list = [tmp_timestamp]

            # 行末まで繰り返す
            for column_data in row_data:
                if column_data =="":
                    # 空白の場合次のカラムへ
                    continue
                column_number += 1

                # COMMANDカラムまでのデータをtmpリストに入れる
                if column_number <= command_column :
                    tmp_output_csv_list.append(column_data)
                else :
                    continue

                # 抽出対象レコードかをチェックする
                # 抽出対象USER、または、user_array指定なしのとき抽出対象レコード
                if column_number == user_column :
                    user_key_flg = True
                    for key_user in user_array:
                        # 抽出対象USER、または、user_array指定なしのとき抽出対象レコード
                        if ( str(column_data) == key_user ):
                            user_key_flg = True
                            break
                        else:
                            user_key_flg = False

                    if user_key_flg == True :
                        pass
                    else:
                        break
            # for文をbreak以外で抜けたらCSV抽出リストに追加する
            else:
                output_csv_list.append(tmp_output_csv_list)


    '''--------------------------
       ファイル書き込み
    --------------------------'''
    # CSV書き出し
    csv_header = ['timestamp','USER','PR','NI','VIRT','RES','SHR','%CPU','%MEM','TIME+','COMMAND']
    with open(f'{output_file_name}','w') as f:
        csv_writer = csv.writer( f, delimiter = ',', lineterminator = '\n') 
        csv_writer.writerow(csv_header)
        csv_writer.writerows(output_csv_list)

2020/07/09 # -*- coding: utf-8 はpython3では非推奨でしたので削除しました。
参考記事:https://qiita.com/KEINOS/items/6efc1147b917d7811b5b

以上です。

感想

ツールもなく、TOPコマンド結果だけ渡されて、「まとめといて」と言われる方々の負担が減りますように。

4
4
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4