Точно сравнивайте каталоги - включая перемещенные файлы

Моя цель состоит в том, чтобы сравнить два каталога точно, включая структуру каталогов и подкаталогов.

Мне это нужно, потому что я хочу контролировать, если что-то в папке E:\path2 был изменен. Для этого случая копия полной папки находится в C:\path1, Если кто-то что-то меняет, это нужно сделать в двух каталогах.

Это важно для нас, потому что если что-то изменить в каталоге (случайно или нет), это может нарушить другие функции в нашей инфраструктуре.

Это сценарий, который я уже написал:

# Compare files for "copy default folder"
# This Script compares the files and folders which are synced to every client.
# Source: https://mcpmag.com/articles/2016/04/14/contents-of-two-folders-with-powershell.aspx

# 1. Compare content and Name of every file recursively
$SourceDocsHash = Get-ChildItem -recurse –Path C:\path1 | foreach  {Get-FileHash –Path $_.FullName}
$DestDocsHash = Get-ChildItem -recurse –Path E:\path2 | foreach  {Get-FileHash –Path $_.FullName}
$ResultDocsHash = (Compare-Object -ReferenceObject $SourceDocsHash -DifferenceObject $DestDocsHash -Property hash -PassThru).Path

# 2. Compare name of every folder recursively
$SourceFolders = Get-ChildItem -recurse –Path C:\path1 #| where {!$_.PSIsContainer}
$DestFolders = Get-ChildItem -recurse –Path E:\path2 #| where {!$_.PSIsContainer}

$CompareFolders = Compare-Object -ReferenceObject $SourceFolders -DifferenceObject $DestFolders -PassThru -Property Name
$ResultFolders = $CompareFolders |  Select-Object FullName

# 3. Check if UNC-Path is reachable
# Source: https://stackru.com/questions/8095638/how-do-i-negate-a-condition-in-powershell
# Printout, if UNC-Path is not available.
if(-Not (Test-Path \\bb-srv-025.ftscu.be\DIP$\Settings\ftsCube\default-folder-on-client\00_ftsCube)){
  $UNCpathReachable = "UNC-Path not reachable and maybe"
}

# 4. Count files for statistics
# Source: https://stackru.com/questions/14714284/count-items-in-a-folder-with-powershell
$count = (Get-ChildItem -recurse –Path E:\path2 | Measure-Object ).Count;

# FINAL: Print out result for check_mk
if($ResultDocsHash -Or $ResultFolders -Or $UNCpathReachable){
  echo "2 copy-default-folders-C-00_ftsCube files-and-folders-count=$count CRITIAL - $UNCpathReachable the following files or folders has been changed: $ResultDocs $ResultFolders (none if empty after ':')"
}
else{
  echo "0 copy-default-folders-C-00_ftsCube files-and-folders-count=$count OK - no files has changed"
}

Я знаю, что вывод не отформатирован, но все в порядке.:-)

Этот скрипт успешно определяет следующие изменения:

  • создать новую папку или новый файл
  • переименовать папку или файл -> это отображается как ошибка, но вывод пуст. Я могу жить с этим. Но, возможно, кто-то видит причину.:-)
  • удалить папку или файл
  • изменить содержимое файла

Этот скрипт НЕ обнаруживает следующие изменения:

  • переместить папку или файл в другую подпапку. Сценарий все еще говорит "все в порядке"

Я много чего пробовал, но не смог решить.

Кто-нибудь может мне помочь, как скрипт может быть расширен, чтобы определить перемещенную папку или файл?

2 ответа

Я думаю, что вам лучше всего использовать класс.NET FileSystemWatcher. Реализовать продвинутую функцию, которая ее использует, нетривиально, но я думаю, что это упростит вам задачу.

Я использовал статью " Отслеживание изменений в папке с помощью PowerShell", когда изучал этот класс. Код автора ниже. Я убрал это так мало, как я мог стоять. (Форматирование кода этой издательской платформы вредит мне.)

Я думаю, что вы хотите запустить это так.

New-FileSystemWatcher -Path E:\path2 -Recurse

Я могу ошибаться.

Function New-FileSystemWatcher  {

    [cmdletbinding()]
    Param (

    [parameter()]
    [string]$Path,

    [parameter()]
    [ValidateSet('Changed', 'Created', 'Deleted', 'Renamed')]
    [string[]]$EventName,

    [parameter()]
    [string]$Filter,

    [parameter()]
    [System.IO.NotifyFilters]$NotifyFilter,

    [parameter()]
    [switch]$Recurse,

    [parameter()]
    [scriptblock]$Action

    )

    $FileSystemWatcher  = New-Object System.IO.FileSystemWatcher

    If (-NOT $PSBoundParameters.ContainsKey('Path')){
        $Path  = $PWD
    }

    $FileSystemWatcher.Path = $Path

    If ($PSBoundParameters.ContainsKey('Filter')) {
        $FileSystemWatcher.Filter = $Filter
    }

    If ($PSBoundParameters.ContainsKey('NotifyFilter')) {
        $FileSystemWatcher.NotifyFilter =  $NotifyFilter
    }

    If ($PSBoundParameters.ContainsKey('Recurse')) {
        $FileSystemWatcher.IncludeSubdirectories =  $True
    }

    If (-NOT $PSBoundParameters.ContainsKey('EventName')){
        $EventName  = 'Changed','Created','Deleted','Renamed'
    }

    If (-NOT $PSBoundParameters.ContainsKey('Action')){
        $Action  = {
            Switch  ($Event.SourceEventArgs.ChangeType) {
                'Renamed'  {
                    $Object  = "{0} was  {1} to {2} at {3}" -f $Event.SourceArgs[-1].OldFullPath,
                    $Event.SourceEventArgs.ChangeType,
                    $Event.SourceArgs[-1].FullPath,
                    $Event.TimeGenerated
                }

                Default  {
                    $Object  = "{0} was  {1} at {2}" -f $Event.SourceEventArgs.FullPath,
                    $Event.SourceEventArgs.ChangeType,
                    $Event.TimeGenerated
                }
            }

            $WriteHostParams  = @{
                ForegroundColor = 'Green'
                BackgroundColor = 'Black'
                Object =  $Object
            }

            Write-Host  @WriteHostParams
        }

    }

    $ObjectEventParams  = @{
        InputObject =  $FileSystemWatcher
        Action =  $Action
    }

    ForEach  ($Item in  $EventName) {
        $ObjectEventParams.EventName = $Item
        $ObjectEventParams.SourceIdentifier =  "File.$($Item)"
        Write-Verbose  "Starting watcher for Event: $($Item)"
        $Null  = Register-ObjectEvent  @ObjectEventParams
    }

} 

Я не думаю, что какой-либо пример, который я нашел в Интернете, говорит вам, как перестать наблюдать за файловой системой. Самый простой способ - просто закрыть окно PowerShell. Но у меня всегда есть 15 вкладок, открытых в каждом из пяти окон PowerShell, и закрытие одной из них создает неудобства.

Вместо этого вы можете использовать Get-Job, чтобы получить Id зарегистрированных событий. Тогда используйте Unregister-Event -SubscriptionId n ну, в общем, отмените регистрацию события, где 'n' представляет число, которое вы найдете в свойстве Id Get-Job.

В общем, вы хотите синхронизировать две папки и отметить все изменения, сделанные в этом:

Я бы предложил вам использовать

Sync-Folder Script

Или же

FreeFile Sync.

Другие вопросы по тегам