(手動目が滑る)pythonでフォルダ複製から変換、差分比較結果作成まで自動で行うTIPS
概要
仕事の中で以下の一連の流れを50フォルダ以上はするという場面に遭遇しました。
- templateフォルダからファイルをコピーして特定の名前にして作成
- そのファイル全てにおいて特定の文字列の置換作業を行う
- Winmergeを用いてtemplateの差分比較をフォルダの全ファイル行う
自分は飽き性なので脳が弱いので同じことをすると目が滑って訳分からなくなりますし、
1フォルダに少なくとも8ファイルはあるので400ファイル以上WinMergeでの比較を行わなければならないと考えたら手が震えてきたので機械に任せることにしました。
方針
1,2に関してはフォルダをコピーしてフォルダ名を変更、vscodeの置換でファイルの対象箇所を置換で完了。
3に関しては、二つのフォルダの全てのファイルを比較した結果を共有する必要があるのですが、
試しにWinMergeインストールしてファイル二つ選択して比較ボタン押してキャプチャ撮って1から3まで一通り完了!おー...って
この時間無駄じゃね?と思った次第で探したところVBでWinMergeの結果をhtmファイルにするという記事があったのでこちらを参考。
VBは知らんのでpythonに変更して実行
1,2を実行するpythonファイルと3を実行するpythonファイル及びループさせるためのPowershellスクリプトのように構成して最終的に1のpythonをpowershell上で叩くことで2, 3も実行されるようにしました。
- templateフォルダからファイルをコピーして特定の名前にして作成
- そのファイル全てにおいて特定の文字列の置換作業を行う
import os
import shutil
from pathlib import Path
import subprocess
# 変換後の値
values = ['12345']
for value in values:
# コピー先のフォルダパス
new_dir = Path(f'./{value}')
# 既存のディレクトリがあれば削除
if new_dir.exists():
shutil.rmtree(new_dir)
# templateフォルダを新しいフォルダにコピー
shutil.copytree('./template', new_dir)
# フォルダ内の全てのファイルを取得
for file_path in new_dir.glob('**/*'):
# ファイルのみを処理
if file_path.is_file():
# ファイルを読み込む
with open(file_path, 'r', newline='', encoding='utf-8') as file:
file_data = file.read()
# 'yyyyy'をlistのvalueに置換
file_data = file_data.replace('yyyyy', value)
# ファイルに書き込む
with open(file_path, 'w', newline='', encoding='utf-8') as file:
file.write(file_data)
print('create_copy_template.py end')
# PowerShellスクリプトのパス
ps_script = Path('./win_merge.ps1')
# PowerShellの実行コマンド
command = f'powershell -ExecutionPolicy RemoteSigned -File "{ps_script}" -values {",".join(values)}'
# PowerShellスクリプトの実行
subprocess.call(command, shell=True)
3をPowershell上でループさせるためのPowershellスクリプト
# win_merge.ps1
# values listを引数で受け取る
param (
[string[]]$values
)
# 関数を定義
function Exec-WinMerge {
param (
[string]$value
)
$base = "{比較対象のフォルダ}"
# 比較したいpathをそれぞれ」指定する。自分の場合は$valueフォルダとtemplateフォルダ
$path1 = Join-Path -Path $base -ChildPath $value
$path2 = Join-Path -Path $base -ChildPath "template"
$outputBase = "{出力対象のフォルダ}"
$output = Join-Path -Path $outputBase -ChildPath "$($value)_win_merge"
python exec_win_merge.py $path1 $path2 $output
}
# 関数を使って実行
foreach ($value in $values) {
Exec-WinMerge $value
}
ここで2でループで回している3のpythonスクリプトを実行します。
3. Winmergeを用いてtemplateの差分比較をフォルダの全ファイル行う
import os
import subprocess
def exec_win_merge(input_dir1, input_dir2, output_dir):
# 出力ディレクトリが存在しない場合、ディレクトリを作成
os.makedirs(output_dir, exist_ok=True)
# ディレクトリ内のファイルを比較
for filename in os.listdir(input_dir1):
# 比較する各ディレクトリのファイルパスを生成
file1 = os.path.join(input_dir1, filename)
file2 = os.path.join(input_dir2, filename)
# 出力ファイルのパスを生成
output_file = os.path.join(output_dir, f"{os.path.splitext(filename)[0]}.htm")
# 両ディレクトリに同名のファイルが存在する場合のみ比較を実行
if os.path.isfile(file1) and os.path.isfile(file2):
# WinMergeUを利用してファイルを比較し、結果を出力ファイルに保存
subprocess.run(f'WinMergeU.exe /e "{file1}" "{file2}" /minimize /noninteractive /u /or "{output_file}"',
shell=True)
# サブディレクトリを再帰的に比較
for subdir in os.listdir(input_dir1):
# 比較する各サブディレクトリのパスを生成
subdir1 = os.path.join(input_dir1, subdir)
subdir2 = os.path.join(input_dir2, subdir)
# 出力サブディレクトリのパスを生成
output_subdir = os.path.join(output_dir, subdir)
# 両ディレクトリに同名のサブディレクトリが存在する場合のみ比較を実行
if os.path.isdir(subdir1) and os.path.isdir(subdir2):
exec_win_merge(subdir1, subdir2, output_subdir)
if __name__ == '__main__':
import sys
# 引数が3つでなければ使い方を出力して終了
if len(sys.argv) != 4:
print(f"Usage: {sys.argv[0]} <input_dir1> <input_dir2> <output_dir>")
sys.exit(1)
# 現在の作業ディレクトリをWinMergeのインストールディレクトリに変更
os.chdir(r"{WinMergeのインストールディレクトリ、Program Filesディレクトリ配下とか}")
# 引数に指定したディレクトリの比較を実行
exec_win_merge(sys.argv[1], sys.argv[2], sys.argv[3])
print("Merge finished")
結果
目視での比較とかそういうやる気根気元気に任せるとかより効率化したほうが幸せになるいい例だなって思いました。
この方法ならtemplateフォルダに修正点が入っても全然苦にならないですし笑
最後に
ゴリ押しでやった方がその時だけは早いかもですが後々のことを考えると調査系にしろ単純作業は機械にさせる方が楽しいです!
どうにかしてエクセルの仕様書作りとかも自動化できないだろうか笑...
参考
Discussion