Param( $PathL, $PathR )
# Set-Alias -Name wo -Value Write-Output
Set-Alias -Name wo -Value Write-Host
Function Check-Parameter()
{
$nResult = 1
if( $PathL -eq $Null -band $PathR -eq $Null )
{
'指定フォルダとそのフォルダのサブフォルダを比較する' | wo
'異なる点のみを示す' | wo
'' | wo
'書式' | wo
'Compare-Tree <フォルダパス(L)> <フォルダパス(R)>' | wo
return 0
}
if( $PathR -eq $Null )
{
'フォルダパス(R)が未指定です。' | wo
$nResult = -1
}
if( $PathL -eq $Null )
{
'フォルダパス(L)が未指定です。' | wo
$nResult = -1
}
if( $PathR -eq $Null )
{
'フォルダパス(R)が未指定です。' | wo
$nResult = -1
}
if( (Test-Path -Path $PathL -PathType Container) -eq $False )
{
'フォルダパス(L)が存在しません。' | wo
$nResult = -1
}
if( (Test-Path -Path $PathR -PathType Container) -eq $False )
{
'フォルダパス(R)が存在しません。' | wo
$nResult = -1
}
return $nResult
}
$nCheckResult = Check-Parameter
if( $nCheckResult -le 0 )
{
return
}
# for Compare-File
# ファイル内容比較用バッファ
$bufferLength = 2048;
$bufferL = New-Object -TypeName byte[] $bufferLength
$bufferR = New-Object -TypeName byte[] $bufferLength
Function Main( [System.IO.DirectoryInfo] $l, [System.IO.DirectoryInfo] $r )
{
Function Compare-File( [System.IO.FileInfo] $l, [System.IO.FileInfo] $r )
{
$result = $False
try
{
$fsL = $l.Open( [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read )
$fsR = $r.Open( [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read )
}
catch
{
if( $fsL -ne $Null )
{
$fsL.Close()
}
if( $fsR -ne $Null )
{
$fsR.Close()
}
'エラー:比較失敗' | wo
$l.FullName | wo
$r.FullName | wo
'' | wo
return $result
}
if( $fsL.Length -eq $fsR.Length )
{
$result = $True
if( $fsL.Length -gt 0 )
{
$brL = [System.IO.BinaryReader]::new( $fsL )
$brR = [System.IO.BinaryReader]::new( $fsR )
$nRemainLength = $fsL.Length
do
{
$nReadLength = $nRemainLength
if( $nReadLength -gt $bufferLength )
{
$nReadLength = $bufferLength
}
$nRemainLength -= $nReadLength
[void] $brL.Read( $bufferL, 0, $nReadLength )
[void] $brR.Read( $bufferR, 0, $nReadLength )
for( $i = 0; $i -lt $nReadLength; $i ++ )
{
if( $bufferL[$i] -ne $bufferR[$i] )
{
$result = $False
$nRemainLength = -1
break
}
}
}while( $nRemainLength -gt 0 )
$brR.Close()
$brL.Close()
}
}
$fsR.Close()
$fsL.Close()
return $result
}
# サブ・フォルダ比較
Function Compare-SubDirectory( [System.IO.DirectoryInfo] $DirL, [System.IO.DirectoryInfo] $DirR )
{
# サブ・フォルダを取得
$SubDirL = $DirL.GetDirectories()
$SubDirR = $DirR.GetDirectories()
# サブ・フォルダを名称順に格納するために連想配列を生成
$CollectDirL = [System.Collections.Generic.SortedDictionary[String, Object]]::new()
$CollectDirR = [System.Collections.Generic.SortedDictionary[String, Object]]::new()
# サブ・フォルダを名称順に連想配列へ登録
$SubDirL | %{ $CollectDirL.Add( $_.Name, @{info=$_ ; hit=$False ; infoR=$Null} ) }
$SubDirR | %{ $CollectDirR.Add( $_.Name, @{info=$_ ; hit=$False } ) }
# 存在マッチング処理
Function Match-SubDirectory( $CollectDir )
{
Begin
{
$counter1 = 0 # 存在しないアイテム数
$counter2 = 0 # 存在するアイテム数
}
Process
{
$v = $Null
if( $CollectDir.TryGetValue($_.info.Name, [ref]$v) )
{
$_.hit = $True
$v.hit = $True
$_.infoR = $v.info
$counter2 ++
}
else
{
$counter1 ++
}
}
End
{
$counter1, $counter2
}
}
# サブ・フォルダの存在マッチング
$countL = $CollectDirL.Values | Match-SubDirectory $CollectDirR
$countR = $CollectDirR.Count - $countL[1]
if( ($countL[0] -ne 0) -bor ($countR -ne 0) )
{
'-- folder 1: [{1,5}] {0}' -f $DirL.FullName ,$CollectDirL.Count | wo
if( $countL[0] -gt 0 )
{
# '---- folder 1 ------' | wo
$CollectDirL.Values | %{ if($_.hit -eq $False){$_.info.Name} } | wo
}
'-- folder 2: [{1,5}] {0}' -f $DirR.FullName ,$CollectDirR.Count | wo
if( $countR -gt 0 )
{
# '---- folder 2 ------' | wo
$CollectDirR.Values | %{ if($_.hit -eq $False){$_.info.Name} } | wo
}
'' | wo
}
# 以降は使用しないため R 側サブ・フォルダ連想配列を破棄
$CollectDirR.Clear()
$CollectDirR = $Null
# マッチングしたサブ・フォルダをサーチ
$CollectDirL.Values | %{ if($_.hit -ne $False){Main $_.info $_.infoR} }
# 以降は使用しないため L 側サブ・フォルダ連想配列を破棄
$CollectDirL.Clear()
$CollectDirL = $Null
}
Function Search-Info( [System.IO.DirectoryInfo] $DirInfo )
{
$search = $DirInfo.EnumerateFiles()
$collect = [System.Collections.Generic.SortedDictionary[String, Object]]::new()
$search | %{ $collect.Add( $_.Name, @{info=$_ ; strDiffDt=' ' ; nEquals=0 ; bHide=$False} ) }
$collect
}
# ファイル収集
$collectL = Search-Info $l
$collectR = Search-Info $r
Function Matching( [System.Collections.Generic.SortedDictionary[String, Object]] $collect )
{
Begin
{
# 一致しているファイル数
$countMatchL = 0
$countMatchR = 0
}
Process
{
$v = $Null
if( $collect.TryGetValue( $_.info.name, [ref] $v ) -ne $False )
{
$nHideL = 0
$nHideR = 0
$result = $_.info.LastWriteTime.CompareTo($v.info.LastWriteTime)
if( $result -eq 0 )
{
$_.bHide = $True
$v.bHide = $True
# $_.strDiffDt = ' '
# $v.strDiffDt = ' '
$nHideL = 1
$nHideR = 1
}
else
{
if( $result -gt 0 )
{
# $_.bHide = $True
$v.bHide = $True
$_.strDiffDt = '1'
$nHideL = 0
$nHideR = 1
}
else
{
$_.bHide = $True
# $v.bHide = $True
$v.strDiffDt = '2'
$nHideL = 1
$nHideR = 0
}
}
$bCompare = Compare-File $_.info $v.info
$_.nEquals = if($bCompare){1}else{-1}
$v.nEquals = if($bCompare){1}else{-1}
if( $bCompare -eq $False )
{
if( $result -eq 0 )
{
$_.bHide = $False
$v.bHide = $True
$nHideL = 0
$nHideR = 1
}
}
$counterMatchL += $nHideL
$counterMatchR += $nHideR
}
}
End
{
$counterMatchL, $counterMatchR
}
}
# ファイル比較
$countMatchL,$countMatchR = $collectL.Values | Matching $collectR
# ファイル比較結果出力
Function Display()
{
Process
{
if( $_.bHide -eq $False )
{
$compareInfo = if($_.nEquals -ne -1){' '}else{' ne '}
'{0}{1}{2}' -f $_.strDiffDt ,$compareInfo ,$_.info.name | wo
}
}
}
# ファイル比較結果出力判定
# ファイル数から完全一致のファイルを除外し、不一致ファイルがあるときに出力
$nDisplayL = $collectL.Values.Count - $countMatchL
$nDisplayR = $collectR.Values.Count - $countMatchR
if( $nDisplayL -gt 0 -bor $nDisplayR -gt 0 )
{
'-- file 1 [{0,5}]: {1}' -f $collectL.Count ,$l.FullName | wo
if( $nDisplayL -gt 0 )
{
$collectL.Values | Display
}
'-- file 2 [{0,5}]: {1}' -f $collectR.Count ,$r.FullName | wo
if( $nDisplayR -gt 0 )
{
$collectR.Values | Display
}
'' | wo
}
# 破棄
$collectL.Clear()
$collectL = $Null
$collectR.Clear()
$collectR = $Null
Compare-SubDirectory $l $r
}
Main (Get-Item $PathL) (Get-Item $PathR)