0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

あるpythonファイルの中で、特定のディレクトリのファイルのbashコマンドのdiffを叩いて取って、画面に表示させたい。想定外の差分がある場合はエラーにさせたい。

Last updated at Posted at 2024-10-09

Pythonスクリプト内でbashコマンドのdiffを使用して特定のディレクトリのファイルの差分を取得し、表示する方法を説明します。

diffコマンドの実行と結果の取得

まず、subprocessモジュールを使用してbashのdiffコマンドを実行し、結果を取得します。

import subprocess
import os

def get_diff(dir1, dir2, filename):
    path1 = os.path.join(dir1, filename)
    path2 = os.path.join(dir2, filename)
    
    try:
        result = subprocess.run(['diff', '-u', path1, path2], 
                                capture_output=True, text=True, check=True)
        return result.stdout
    except subprocess.CalledProcessError as e:
        if e.returncode == 1:  # diff found differences
            return e.output
        else:  # other error occurred
            raise RuntimeError(f"Error comparing files: {e}")

この関数は、2つのディレクトリ内の同名ファイルのdiffを取得します。

ディレクトリ内のファイル比較

次に、指定されたディレクトリ内のすべてのファイルを比較する関数を作成します。

def compare_directories(dir1, dir2):
    diffs = {}
    files = os.listdir(dir1)
    for filename in files:
        if os.path.isfile(os.path.join(dir1, filename)):
            diff = get_diff(dir1, dir2, filename)
            if diff:
                diffs[filename] = diff
    return diffs

この関数は、dir1内のすべてのファイルをdir2内の対応するファイルと比較し、差分があるファイルのdiffを辞書に格納します。

結果の表示とエラー処理

最後に、結果を表示し、想定外の差分がある場合にエラーを発生させる関数を作成します。

def display_diffs_and_check(dir1, dir2, expected_diffs=[]):
    diffs = compare_directories(dir1, dir2)
    
    if not diffs:
        print("No differences found.")
        return
    
    for filename, diff in diffs.items():
        print(f"Differences in {filename}:")
        print(diff)
    
    unexpected_diffs = set(diffs.keys()) - set(expected_diffs)
    if unexpected_diffs:
        raise Exception(f"Unexpected differences found in files: {', '.join(unexpected_diffs)}")

この関数は、ディレクトリ間の差分を表示し、expected_diffsリストに含まれていない差分がある場合に例外を発生させます。

使用例

以下は、これらの関数を使用する例です:

if __name__ == "__main__":
    dir1 = "/path/to/directory1"
    dir2 = "/path/to/directory2"
    expected_diffs = ["config.ini", "readme.txt"]
    
    try:
        display_diffs_and_check(dir1, dir2, expected_diffs)
        print("All differences are as expected.")
    except Exception as e:
        print(f"Error: {e}")

このスクリプトは、2つのディレクトリを比較し、bashのdiffコマンドを使用して差分を表示します。expected_diffsリストに含まれていないファイルに差分がある場合、エラーメッセージを表示します。

このアプローチにより、Pythonスクリプト内でbashのdiffコマンドを使用して特定のディレクトリのファイルの差分を取得し、画面に表示し、想定外の差分がある場合にエラーを発生させることができます。

ホワイトリストとの照合もしたい場合

import subprocess
import os

# ディレクトリの定義
# dir1: 変更前のログファイルが格納されているディレクトリ
# dir2: 変更後のログファイルが格納されているディレクトリ
dir1 = "/path/to/before_logs"
dir2 = "/path/to/after_logs"

def get_diff(file1, file2):
    """
    2つのファイルの差分を取得する関数
    
    Args:
    file1 (str): 比較元のファイルパス
    file2 (str): 比較先のファイルパス
    
    Returns:
    str: diffコマンドの出力結果
    
    Raises:
    RuntimeError: diffコマンドが失敗した場合
    """
    try:
        # diffコマンドを実行し、結果を取得
        result = subprocess.run(['diff', file1, file2], 
                                capture_output=True, text=True, check=True)
        return result.stdout
    except subprocess.CalledProcessError as e:
        if e.returncode == 1:  # diffコマンドが差分を見つけた場合
            return e.output
        else:  # その他のエラーが発生した場合
            raise RuntimeError(f"Error comparing files: {e}")

def check_whitelist(diff, whitelist):
    """
    差分がホワイトリストに含まれているかチェックする関数
    
    Args:
    diff (str): diffコマンドの出力結果
    whitelist (list): 許可される差分のリスト
    
    Returns:
    bool: すべての差分がホワイトリストに含まれている場合はTrue、そうでない場合はFalse
    """
    for line in diff.split('\n'):
        # '<' は削除された行、'>' は追加された行を示す
        if line.startswith('<') or line.startswith('>'):
            # 差分行がホワイトリストのいずれにも含まれていない場合はFalseを返す
            if not any(white in line for white in whitelist):
                return False
    return True

def compare_directories(whitelist):
    """
    2つのディレクトリ内のファイルを比較し、結果を返す関数
    
    Args:
    whitelist (list): 許可される差分のリスト
    
    Returns:
    dict: ファイル名をキー、差分内容とステータスを値とする辞書
    """
    results = {}
    files = os.listdir(dir1)
    for filename in files:
        before_file = os.path.join(dir1, filename)
        after_file = os.path.join(dir2, filename)
        if os.path.isfile(before_file) and os.path.isfile(after_file):
            diff = get_diff(before_file, after_file)
            if diff:
                is_ok = check_whitelist(diff, whitelist)
                results[filename] = {
                    'diff': diff,
                    'status': 'OK' if is_ok else 'NG'
                }
    return results

def display_results(results):
    """
    比較結果を表示する関数
    
    Args:
    results (dict): compare_directories関数の返り値
    """
    for filename, result in results.items():
        print(f"File: {filename}")
        print(f"Status: {result['status']}")
        print("Diff:")
        print(result['diff'])
        print("-" * 40)  # 区切り線

if __name__ == "__main__":
    # ホワイトリストの定義
    # 許可される差分の文字列をリストで指定
    whitelist = ["許可される差分1", "許可される差分2", "許可される差分3"]
    
    # ディレクトリの比較を実行
    results = compare_directories(whitelist)
    
    # 結果の表示
    display_results(results)
0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?