進捗状況が表示される重複ファイルチェックツールを作りたい
SynologyとかのNASなら標準で搭載されている重複ファイルをチェックするツール。
Windowsファイルサーバ機能でも搭載されているけど、一般ユーザーがもっと手軽に確認できるようにしたいと思い、社内で標準搭載されているWindows Powershellを利用して作成した。
搭載機能
・重複ファイルのチェックをハッシュ値比較で行う。
・結果はCSVファイルで出力する。ファイル名に実行年月日時分秒をつける。
・削除はしない(別ツールで実装。また記事書く)
・ファイル重複チェックはファイル数が多かったりファイルサイズが大きかったりすると時間がかかるので、進捗状況が表示されるようにする。
・対象フォルダをテキストボックスで指定できるようにする。
・後からエラーなどを終えるように実行ログを別ファイルとして吐き出す。
※別途関数にしたほうが良かった気がする。要改善
コード
絶対改善余地があるけどこんな感じ。
#
# 重複するファイルを探すスクリプト
#
# ファイル名:Search-DuplicateFile.ps1
# 調査対象フォルダの初期化
$SearchFolder = ""
# 連想配列の初期化
$array = @{}
# CSVテーブルの用意
$CSVdatas = @()
# 日付の取得
$date = Get-Date -Format "yyyyMMdd";
# 時間の取得
$time = Get-Date -Format "HHmmss";
# 調査結果を出力するフォルダ
$scriptPath = $MyInvocation.MyCommand.Path
$scriptPath_split = Split-Path -Parent $scriptPath
$ResultFolder = ($scriptPath_split+"\結果")
# 調査結果出力結果フォルダがなかったら作成
if( -not (Test-Path $ResultFolder) ) {
New-Item $ResultFolder -Type Directory
}
# 調査結果出力ファイル名
$ResultFile = ( "\DuplicateFile_"+$date+"_"+$time+".csv" )
$ExecuteLogFile = ( "\ExecuteLog_"+$date+"_"+$time+".log" )
#調査結果出力フォルダとファイル名のマージ
$Result = Join-Path $ResultFolder $ResultFile
$ExecuteLog = Join-Path $ResultFolder $ExecuteLogFile
# アセンブリ読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# フォーム作成
$Form = New-Object System.Windows.Forms.Form
$Form.Size = New-Object System.Drawing.Size(400,130)
$Form.Text = "重複ファイル調査ツール"
# ラベル作成
$LabelFilePath = New-Object System.Windows.Forms.Label
$LabelFilePath.Location = New-Object System.Drawing.Point(20,10)
$LabelFilePath.Size = New-Object System.Drawing.Size(300,20)
$LabelFilePath.Text = "重複ファイル調査対象フォルダのパスを入力してください"
$Form.Controls.Add($LabelFilePath)
# 入力用テキストボックス
$TextBoxFilePath = New-Object System.Windows.Forms.TextBox
$TextBoxFilePath.Location = New-Object System.Drawing.Point(20,30)
$TextBoxFilePath.Size = New-Object System.Drawing.Size(300,20)
$Form.Controls.Add($TextBoxFilePath)
# 参照ボタン
$ButtonFilePath = New-Object System.Windows.Forms.Button
$ButtonFilePath.Location = New-Object System.Drawing.Point(320,30)
$ButtonFilePath.Size = New-Object System.Drawing.Size(40,20)
$ButtonFilePath.Text = "参照"
$Form.Controls.Add($ButtonFilePath)
# OKボタン
$ButtonOK = New-Object System.Windows.Forms.Button
$ButtonOK.Location = New-Object System.Drawing.Point(230,60)
$ButtonOK.Size = New-Object System.Drawing.Size(60,20)
$ButtonOK.Text = "OK"
$Form.Controls.Add($ButtonOK)
# Cancelボタン
$ButtonCancel = New-Object System.Windows.Forms.Button
$ButtonCancel.Location = New-Object System.Drawing.Point(300,60)
$ButtonCancel.Size = New-Object System.Drawing.Size(60,20)
$ButtonCancel.Text = "キャンセル"
$ButtonCancel.DialogResult = "Cancel"
$Form.Controls.Add($ButtonCancel)
# 参照ボタンをクリック時の動作
$ButtonFilePath.add_click{
#ダイアログを表示しファイルを選択する
$Dialog = New-Object System.Windows.Forms.FolderBrowserDialog
if($Dialog.ShowDialog() -eq "OK"){
$TextBoxFilePath.Text = $Dialog.SelectedPath
}
}
# OKボタンをクリック時の動作
$ButtonOK.add_click{
#ファイルパスが入力されていないときは背景を黄色にする
if($TextBoxFilePath.text -eq ""){
$TextBoxFilePath.BackColor = "yellow"
}else{
$Form.DialogResult = "OK"
}
}
Start-Transcript $ExecuteLog
#フォームを表示し処理が完了したらファイルパスを返す
$FormResult = $Form.ShowDialog()
if($FormResult -eq "OK"){
$SearchFolder = $TextBoxFilePath.text
echo $SearchFolder"を調査中…ファイルが多いと時間かかります"
###進捗表示用###
echo "ファイル数取得中"
$GetSearchFolder = Get-ChildItem -File -Recurse $SearchFolder
$fileCount = $GetSearchFolder.Length
$counter = 0
echo ("ファイル数="+$fileCount)
echo "重複チェック開始"
echo ""
$denominator = "/"+[string]$fileCount
###ここからハッシュ比較開始###
$GetSearchFolder | Sort-Object Length | Get-FileHash -Algorithm MD5 | % {
$counter ++;
Write-Progress -activity "進捗状況" -status $counter$denominator -percentComplete ($counter / $fileCount * 100)
if ($array.ContainsKey($_.Hash)) {
# 既存に存在するハッシュ値(つまり重複)
} else {
# 新規ハッシュ値なので、要素の追加
$d = $array.Add($_.Hash, (New-Object System.Collections.ArrayList))
}
# ハッシュ値に対するファイルパスの格納(重複の場合、Valueが増えていく)
$d = $array[$_.Hash].Add($_.Path)
}
$array.GetEnumerator() | ? {
$_.Value.Count -gt 1
} | % {
$hash = $_.Name
$files = $_.Value
$files | % {
$CSVdata = New-Object PSObject | Select-Object Hash,FileSizeKB,FileSizeMB,CreationTime,LastWriteTime,Path
$path = $_
$Item = Get-Item $path
$filesizeKB = [Math]::Round(($Item).length/1KB, 1, [MidpointRounding]::AwayFromZero);
$filesizeMB = [Math]::Round(($Item).length/1MB, 1, [MidpointRounding]::AwayFromZero);
$CRTime = ($Item).CreationTime
$LWTime = ($Item).LastWriteTime
$CSVdata.Hash = $hash
$CSVdata.FileSizeKB = $filesizeKB
$CSVdata.FileSizeMB = $filesizeMB
$CSVdata.CreationTime = $CRTime
$CSVdata.LastWriteTime = $LWTime
$CSVdata.Path =$path
$CSVdatas += $CSVdata
}
}
$CSVdatas | Sort-Object FileSizeKB -Descend | Export-CSV $Result -Encoding Default
Invoke-item $Result
} else {
}
Stop-Transcript
実行イメージ
注意点と補足
・PowerShellが実行可能な環境である必要があります。
・3700ファイルを調査するのに大体4分くらいかかります。
※実行環境や個々のファイルサイズにより変化
・共有フォルダなどで利用する場合は認証済みである必要があります。
・仕様により処理後半に近づくにつれ進捗の進みが緩やかになる場合があります。
※ファイルサイズの小さいものから処理するようソートをかけているため
改版履歴
2020/09/04 初版公開
コメント