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)